CCodec: Episode IV --- Raw Video Buffers
Define and handle new buffer types for raw video buffers.
Bug: 69376489
Test: adb shell setprop debug.stagefright.ccodec yes
Test: adb shell setprop debug.stagefright.omx_default_rank 1000
Test: adb shell killall mediaserver
Test: atest CtsMediaTestCases:ImageReaderDecoderTest#testOtherH264ImageReader
Change-Id: I9d1061287e4c46526170ce395f826eccf4def4b7
(cherry picked from commit 156398391ed8d14b527328cb276922168c8d91a8)
diff --git a/media/libstagefright/BufferImpl.cpp b/media/libstagefright/BufferImpl.cpp
index d2eee33..7b3fa02 100644
--- a/media/libstagefright/BufferImpl.cpp
+++ b/media/libstagefright/BufferImpl.cpp
@@ -70,6 +70,10 @@
if (const_cast<Codec2Buffer *>(this)->base() == nullptr) {
return false;
}
+ if (!buffer) {
+ // Nothing to copy, so we can copy by doing nothing.
+ return true;
+ }
if (buffer->data().type() != C2BufferData::LINEAR) {
return false;
}
@@ -89,7 +93,7 @@
bool Codec2Buffer::copyLinear(const std::shared_ptr<C2Buffer> &buffer) {
// We assume that all canCopyLinear() checks passed.
- if (buffer->data().linearBlocks().size() == 0u) {
+ if (!buffer || buffer->data().linearBlocks().size() == 0u) {
setRange(0, 0);
return true;
}
@@ -207,4 +211,445 @@
return std::move(mBufferRef);
}
+// GraphicView2MediaImageConverter
+
+namespace {
+
+class GraphicView2MediaImageConverter {
+public:
+ explicit GraphicView2MediaImageConverter(const C2GraphicView &view)
+ : mInitCheck(NO_INIT),
+ mView(view),
+ mWidth(view.width()),
+ mHeight(view.height()),
+ mAllocatedDepth(0),
+ mBackBufferSize(0),
+ mMediaImage(new ABuffer(sizeof(MediaImage2))) {
+ if (view.error() != C2_OK) {
+ ALOGD("Converter: view.error() = %d", view.error());
+ mInitCheck = BAD_VALUE;
+ return;
+ }
+ MediaImage2 *mediaImage = (MediaImage2 *)mMediaImage->base();
+ const C2PlanarLayout &layout = view.layout();
+ if (layout.numPlanes == 0) {
+ ALOGD("Converter: 0 planes");
+ mInitCheck = BAD_VALUE;
+ return;
+ }
+ mAllocatedDepth = layout.planes[0].allocatedDepth;
+ uint32_t bitDepth = layout.planes[0].bitDepth;
+
+ switch (layout.type) {
+ case C2PlanarLayout::TYPE_YUV:
+ mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_YUV; break;
+ case C2PlanarLayout::TYPE_YUVA:
+ mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_YUVA; break;
+ case C2PlanarLayout::TYPE_RGB:
+ mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_RGB; break;
+ case C2PlanarLayout::TYPE_RGBA:
+ mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_RGBA; break;
+ default:
+ mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_UNKNOWN; break;
+ }
+ mediaImage->mNumPlanes = layout.numPlanes;
+ mediaImage->mWidth = mWidth;
+ mediaImage->mHeight = mHeight;
+ mediaImage->mBitDepth = bitDepth;
+ mediaImage->mBitDepthAllocated = mAllocatedDepth;
+
+ uint32_t bufferSize = 0;
+ for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+ const C2PlaneInfo &plane = layout.planes[i];
+ if (plane.rightShift != 0) {
+ ALOGV("rightShift value of %u unsupported", plane.rightShift);
+ mInitCheck = BAD_VALUE;
+ return;
+ }
+ if (plane.endianness != C2PlaneInfo::NATIVE) {
+ ALOGV("endianness value of %u unsupported", plane.endianness);
+ mInitCheck = BAD_VALUE;
+ return;
+ }
+ if (plane.allocatedDepth != mAllocatedDepth || plane.bitDepth != bitDepth) {
+ ALOGV("different allocatedDepth/bitDepth per plane unsupported");
+ mInitCheck = BAD_VALUE;
+ return;
+ }
+ bufferSize += mWidth * mHeight
+ / plane.rowSampling / plane.colSampling * (plane.allocatedDepth / 8);
+ }
+
+ mBackBufferSize = bufferSize;
+ mInitCheck = OK;
+ }
+
+ status_t initCheck() const { return mInitCheck; }
+
+ uint32_t backBufferSize() const { return mBackBufferSize; }
+
+ /**
+ * Convert C2GraphicView to MediaImage2. Note that if not wrapped, the content
+ * is not copied over in this function --- the caller should use
+ * CopyGraphicView2MediaImage() function to do that explicitly.
+ *
+ * \param view[in] source C2GraphicView object.
+ * \param alloc[in] allocator function for ABuffer.
+ * \param mediaImage[out] destination MediaImage2 object.
+ * \param buffer[out] new buffer object.
+ * \param wrapped[out] whether we wrapped around existing map or
+ * allocated a new buffer
+ *
+ * \return true if conversion succeeds,
+ * false otherwise; all output params should be ignored.
+ */
+ sp<ABuffer> wrap() {
+ MediaImage2 *mediaImage = getMediaImage();
+ const C2PlanarLayout &layout = mView.layout();
+ if (layout.numPlanes == 1) {
+ const C2PlaneInfo &plane = layout.planes[0];
+ ssize_t offset = plane.minOffset(mWidth, mHeight);
+ mediaImage->mPlane[0].mOffset = -offset;
+ mediaImage->mPlane[0].mColInc = plane.colInc;
+ mediaImage->mPlane[0].mRowInc = plane.rowInc;
+ mediaImage->mPlane[0].mHorizSubsampling = plane.colSampling;
+ mediaImage->mPlane[0].mVertSubsampling = plane.rowSampling;
+ return new ABuffer(
+ const_cast<uint8_t *>(mView.data()[0] + offset),
+ plane.maxOffset(mWidth, mHeight) - offset + 1);
+ }
+ const uint8_t *minPtr = mView.data()[0];
+ const uint8_t *maxPtr = mView.data()[0];
+ int32_t planeSize = 0;
+ for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+ const C2PlaneInfo &plane = layout.planes[i];
+ ssize_t minOffset = plane.minOffset(mWidth, mHeight);
+ ssize_t maxOffset = plane.maxOffset(mWidth, mHeight);
+ if (minPtr > mView.data()[i] + minOffset) {
+ minPtr = mView.data()[i] + minOffset;
+ }
+ if (maxPtr < mView.data()[i] + maxOffset) {
+ maxPtr = mView.data()[i] + maxOffset;
+ }
+ planeSize += std::abs(plane.rowInc) * mHeight
+ / plane.rowSampling / plane.colSampling * (mAllocatedDepth / 8);
+ }
+
+ if ((maxPtr - minPtr + 1) <= planeSize) {
+ // FIXME: this is risky as reading/writing data out of bound results in
+ // an undefined behavior.
+ for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+ const C2PlaneInfo &plane = layout.planes[i];
+ mediaImage->mPlane[i].mOffset = mView.data()[i] - minPtr;
+ mediaImage->mPlane[i].mColInc = plane.colInc;
+ mediaImage->mPlane[i].mRowInc = plane.rowInc;
+ mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling;
+ mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling;
+ }
+ return new ABuffer(const_cast<uint8_t *>(minPtr), maxPtr - minPtr + 1);
+ }
+
+ return nullptr;
+ }
+
+ bool setBackBuffer(const sp<ABuffer> &backBuffer) {
+ if (backBuffer->capacity() < mBackBufferSize) {
+ return false;
+ }
+ backBuffer->setRange(0, mBackBufferSize);
+
+ const C2PlanarLayout &layout = mView.layout();
+ MediaImage2 *mediaImage = getMediaImage();
+ uint32_t offset = 0;
+ // TODO: keep interleaved planes together
+ for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+ const C2PlaneInfo &plane = layout.planes[i];
+ mediaImage->mPlane[i].mOffset = offset;
+ mediaImage->mPlane[i].mColInc = mAllocatedDepth / 8;
+ mediaImage->mPlane[i].mRowInc =
+ mediaImage->mPlane[i].mColInc * mWidth / plane.colSampling;
+ mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling;
+ mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling;
+ offset += mediaImage->mPlane[i].mRowInc * mHeight / plane.rowSampling;
+ }
+ mBackBuffer = backBuffer;
+ return true;
+ }
+
+ /**
+ * Copy C2GraphicView to MediaImage2. This function assumes that |mediaImage| is
+ * an output from GraphicView2MediaImage(), so it mostly skips sanity check.
+ *
+ * \param view[in] source C2GraphicView object.
+ * \param mediaImage[in] destination MediaImage2 object.
+ * \param buffer[out] new buffer object.
+ */
+ void copy() {
+ // TODO: more efficient copying --- e.g. one row at a time, copying
+ // interleaved planes together, etc.
+ const C2PlanarLayout &layout = mView.layout();
+ MediaImage2 *mediaImage = getMediaImage();
+ uint8_t *dst = mBackBuffer->base();
+ for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+ const C2PlaneInfo &plane = layout.planes[i];
+ const uint8_t *src = mView.data()[i];
+ int32_t planeW = mWidth / plane.colSampling;
+ int32_t planeH = mHeight / plane.rowSampling;
+ for (int32_t row = 0; row < planeH; ++row) {
+ for(int32_t col = 0; col < planeW; ++col) {
+ memcpy(dst, src, mAllocatedDepth / 8);
+ dst += mediaImage->mPlane[i].mColInc;
+ src += plane.colInc;
+ }
+ dst -= mediaImage->mPlane[i].mColInc * planeW;
+ dst += mediaImage->mPlane[i].mRowInc;
+ src -= plane.colInc * planeW;
+ src += plane.rowInc;
+ }
+ }
+ }
+
+ const sp<ABuffer> &imageData() const { return mMediaImage; }
+
+private:
+ status_t mInitCheck;
+
+ const C2GraphicView mView;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mAllocatedDepth;
+ uint32_t mBackBufferSize;
+ sp<ABuffer> mMediaImage;
+ std::function<sp<ABuffer>(size_t)> mAlloc;
+
+ sp<ABuffer> mBackBuffer;
+
+ MediaImage2 *getMediaImage() {
+ return (MediaImage2 *)mMediaImage->base();
+ }
+};
+
+} // namespace
+
+// GraphicBlockBuffer
+
+// static
+sp<GraphicBlockBuffer> GraphicBlockBuffer::Allocate(
+ const sp<AMessage> &format,
+ const std::shared_ptr<C2GraphicBlock> &block,
+ std::function<sp<ABuffer>(size_t)> alloc) {
+ C2GraphicView view(block->map().get());
+ if (view.error() != C2_OK) {
+ ALOGD("C2GraphicBlock::map failed: %d", view.error());
+ return nullptr;
+ }
+ GraphicView2MediaImageConverter converter(view);
+ if (converter.initCheck() != OK) {
+ ALOGD("Converter init failed: %d", converter.initCheck());
+ return nullptr;
+ }
+ bool wrapped = true;
+ sp<ABuffer> buffer = converter.wrap();
+ if (buffer == nullptr) {
+ buffer = alloc(converter.backBufferSize());
+ if (!converter.setBackBuffer(buffer)) {
+ ALOGD("Converter failed to set back buffer");
+ return nullptr;
+ }
+ wrapped = false;
+ }
+ return new GraphicBlockBuffer(
+ format,
+ buffer,
+ std::move(view),
+ block,
+ converter.imageData(),
+ wrapped);
+}
+
+GraphicBlockBuffer::GraphicBlockBuffer(
+ const sp<AMessage> &format,
+ const sp<ABuffer> &buffer,
+ C2GraphicView &&view,
+ const std::shared_ptr<C2GraphicBlock> &block,
+ const sp<ABuffer> &imageData,
+ bool wrapped)
+ : Codec2Buffer(format, buffer),
+ mView(view),
+ mBlock(block),
+ mImageData(imageData),
+ mWrapped(wrapped) {
+ meta()->setBuffer("image-data", imageData);
+}
+
+std::shared_ptr<C2Buffer> GraphicBlockBuffer::asC2Buffer() {
+ uint32_t width = mView.width();
+ uint32_t height = mView.height();
+ if (!mWrapped) {
+ MediaImage2 *mediaImage = imageData();
+ const C2PlanarLayout &layout = mView.layout();
+ for (uint32_t i = 0; i < mediaImage->mNumPlanes; ++i) {
+ const C2PlaneInfo &plane = layout.planes[i];
+ int32_t planeW = width / plane.colSampling;
+ int32_t planeH = height / plane.rowSampling;
+ const uint8_t *src = base() + mediaImage->mPlane[i].mOffset;
+ uint8_t *dst = mView.data()[i];
+ for (int32_t row = 0; row < planeH; ++row) {
+ for (int32_t col = 0; col < planeW; ++col) {
+ memcpy(dst, src, mediaImage->mBitDepthAllocated / 8);
+ src += mediaImage->mPlane[i].mColInc;
+ dst += plane.colInc;
+ }
+ src -= mediaImage->mPlane[i].mColInc * planeW;
+ dst -= plane.colInc * planeW;
+ src += mediaImage->mPlane[i].mRowInc;
+ dst += plane.rowInc;
+ }
+ }
+ }
+ return C2Buffer::CreateGraphicBuffer(
+ mBlock->share(C2Rect(width, height), C2Fence()));
+}
+
+// ConstGraphicBlockBuffer
+
+// static
+sp<ConstGraphicBlockBuffer> ConstGraphicBlockBuffer::Allocate(
+ const sp<AMessage> &format,
+ const std::shared_ptr<C2Buffer> &buffer,
+ std::function<sp<ABuffer>(size_t)> alloc) {
+ if (!buffer
+ || buffer->data().type() != C2BufferData::GRAPHIC
+ || buffer->data().graphicBlocks().size() != 1u) {
+ ALOGD("C2Buffer precond fail");
+ return nullptr;
+ }
+ std::unique_ptr<const C2GraphicView> view(std::make_unique<const C2GraphicView>(
+ buffer->data().graphicBlocks()[0].map().get()));
+ std::unique_ptr<const C2GraphicView> holder;
+
+ GraphicView2MediaImageConverter converter(*view);
+ if (converter.initCheck() != OK) {
+ ALOGD("Converter init failed: %d", converter.initCheck());
+ return nullptr;
+ }
+ bool wrapped = true;
+ sp<ABuffer> aBuffer = converter.wrap();
+ if (aBuffer == nullptr) {
+ aBuffer = alloc(converter.backBufferSize());
+ if (!converter.setBackBuffer(aBuffer)) {
+ ALOGD("Converter failed to set back buffer");
+ return nullptr;
+ }
+ wrapped = false;
+ converter.copy();
+ // We don't need the view.
+ holder = std::move(view);
+ }
+ return new ConstGraphicBlockBuffer(
+ format,
+ aBuffer,
+ std::move(view),
+ buffer,
+ converter.imageData(),
+ wrapped);
+}
+
+// static
+sp<ConstGraphicBlockBuffer> ConstGraphicBlockBuffer::AllocateEmpty(
+ const sp<AMessage> &format,
+ std::function<sp<ABuffer>(size_t)> alloc) {
+ int32_t width, height;
+ if (!format->findInt32("width", &width)
+ || !format->findInt32("height", &height)) {
+ ALOGD("format had no width / height");
+ return nullptr;
+ }
+ sp<ABuffer> aBuffer(alloc(width * height * 4));
+ return new ConstGraphicBlockBuffer(
+ format,
+ aBuffer,
+ nullptr,
+ nullptr,
+ nullptr,
+ false);
+}
+
+ConstGraphicBlockBuffer::ConstGraphicBlockBuffer(
+ const sp<AMessage> &format,
+ const sp<ABuffer> &aBuffer,
+ std::unique_ptr<const C2GraphicView> &&view,
+ const std::shared_ptr<C2Buffer> &buffer,
+ const sp<ABuffer> &imageData,
+ bool wrapped)
+ : Codec2Buffer(format, aBuffer),
+ mView(std::move(view)),
+ mBufferRef(buffer),
+ mWrapped(wrapped) {
+ if (imageData != nullptr) {
+ meta()->setBuffer("image-data", imageData);
+ }
+}
+
+std::shared_ptr<C2Buffer> ConstGraphicBlockBuffer::asC2Buffer() {
+ mView.reset();
+ return std::move(mBufferRef);
+}
+
+bool ConstGraphicBlockBuffer::canCopy(const std::shared_ptr<C2Buffer> &buffer) const {
+ if (mWrapped || mBufferRef) {
+ ALOGD("ConstGraphicBlockBuffer::canCopy: %swrapped ; buffer ref %s",
+ mWrapped ? "" : "not ", mBufferRef ? "exists" : "doesn't exist");
+ return false;
+ }
+ if (!buffer) {
+ // Nothing to copy, so we can copy by doing nothing.
+ return true;
+ }
+ if (buffer->data().type() != C2BufferData::GRAPHIC) {
+ ALOGD("ConstGraphicBlockBuffer::canCopy: buffer precondition unsatisfied");
+ return false;
+ }
+ if (buffer->data().graphicBlocks().size() == 0) {
+ return true;
+ } else if (buffer->data().graphicBlocks().size() != 1u) {
+ ALOGD("ConstGraphicBlockBuffer::canCopy: too many blocks");
+ return false;
+ }
+ GraphicView2MediaImageConverter converter(
+ buffer->data().graphicBlocks()[0].map().get());
+ if (converter.initCheck() != OK) {
+ ALOGD("ConstGraphicBlockBuffer::canCopy: converter init failed: %d", converter.initCheck());
+ return false;
+ }
+ if (converter.backBufferSize() > capacity()) {
+ ALOGD("ConstGraphicBlockBuffer::canCopy: insufficient capacity: req %u has %zu",
+ converter.backBufferSize(), capacity());
+ return false;
+ }
+ return true;
+}
+
+bool ConstGraphicBlockBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) {
+ if (!buffer || buffer->data().graphicBlocks().size() == 0) {
+ setRange(0, 0);
+ return true;
+ }
+ GraphicView2MediaImageConverter converter(
+ buffer->data().graphicBlocks()[0].map().get());
+ if (converter.initCheck() != OK) {
+ ALOGD("ConstGraphicBlockBuffer::copy: converter init failed: %d", converter.initCheck());
+ return false;
+ }
+ sp<ABuffer> aBuffer = new ABuffer(base(), capacity());
+ if (!converter.setBackBuffer(aBuffer)) {
+ ALOGD("ConstGraphicBlockBuffer::copy: set back buffer failed");
+ return false;
+ }
+ converter.copy();
+ meta()->setBuffer("image-data", converter.imageData());
+ mBufferRef = buffer;
+ return true;
+}
+
} // namespace android
diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp
index a144382..0bdd808 100644
--- a/media/libstagefright/CCodec.cpp
+++ b/media/libstagefright/CCodec.cpp
@@ -404,6 +404,14 @@
if (audio) {
outputFormat->setInt32("channel-count", 2);
outputFormat->setInt32("sample-rate", 44100);
+ } else {
+ int32_t tmp;
+ if (msg->findInt32("width", &tmp)) {
+ outputFormat->setInt32("width", tmp);
+ }
+ if (msg->findInt32("height", &tmp)) {
+ outputFormat->setInt32("height", tmp);
+ }
}
}
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
index de78c80..65d637b 100644
--- a/media/libstagefright/CCodecBufferChannel.cpp
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -182,21 +182,158 @@
const static size_t kMinBufferArraySize = 16;
const static size_t kLinearBufferSize = 524288;
-sp<LinearBlockBuffer> allocateLinearBuffer(
+/**
+ * Simple local buffer pool backed by std::vector.
+ */
+class LocalBufferPool : public std::enable_shared_from_this<LocalBufferPool> {
+public:
+ /**
+ * Create a new LocalBufferPool object.
+ *
+ * \param poolCapacity max total size of buffers managed by this pool.
+ *
+ * \return a newly created pool object.
+ */
+ static std::shared_ptr<LocalBufferPool> Create(size_t poolCapacity) {
+ return std::shared_ptr<LocalBufferPool>(new LocalBufferPool(poolCapacity));
+ }
+
+ /**
+ * Return an ABuffer object whose size is at least |capacity|.
+ *
+ * \param capacity requested capacity
+ * \return nullptr if the pool capacity is reached
+ * an ABuffer object otherwise.
+ */
+ sp<ABuffer> newBuffer(size_t capacity) {
+ Mutex::Autolock lock(mMutex);
+ auto it = std::find_if(
+ mPool.begin(), mPool.end(),
+ [capacity](const std::vector<uint8_t> &vec) {
+ return vec.capacity() >= capacity;
+ });
+ if (it != mPool.end()) {
+ sp<ABuffer> buffer = new VectorBuffer(std::move(*it), shared_from_this());
+ mPool.erase(it);
+ return buffer;
+ }
+ if (mUsedSize + capacity > mPoolCapacity) {
+ while (!mPool.empty()) {
+ mUsedSize -= mPool.back().capacity();
+ mPool.pop_back();
+ }
+ if (mUsedSize + capacity > mPoolCapacity) {
+ ALOGD("mUsedSize = %zu, capacity = %zu, mPoolCapacity = %zu",
+ mUsedSize, capacity, mPoolCapacity);
+ return nullptr;
+ }
+ }
+ std::vector<uint8_t> vec(capacity);
+ mUsedSize += vec.capacity();
+ return new VectorBuffer(std::move(vec), shared_from_this());
+ }
+
+private:
+ /**
+ * ABuffer backed by std::vector.
+ */
+ class VectorBuffer : public ::android::ABuffer {
+ public:
+ /**
+ * Construct a VectorBuffer by taking the ownership of supplied vector.
+ *
+ * \param vec backing vector of the buffer. this object takes
+ * ownership at construction.
+ * \param pool a LocalBufferPool object to return the vector at
+ * destruction.
+ */
+ VectorBuffer(std::vector<uint8_t> &&vec, const std::shared_ptr<LocalBufferPool> &pool)
+ : ABuffer(vec.data(), vec.capacity()),
+ mVec(std::move(vec)),
+ mPool(pool) {
+ }
+
+ ~VectorBuffer() override {
+ std::shared_ptr<LocalBufferPool> pool = mPool.lock();
+ if (pool) {
+ // If pool is alive, return the vector back to the pool so that
+ // it can be recycled.
+ pool->returnVector(std::move(mVec));
+ }
+ }
+
+ private:
+ std::vector<uint8_t> mVec;
+ std::weak_ptr<LocalBufferPool> mPool;
+ };
+
+ Mutex mMutex;
+ size_t mPoolCapacity;
+ size_t mUsedSize;
+ std::list<std::vector<uint8_t>> mPool;
+
+ /**
+ * Private constructor to prevent constructing non-managed LocalBufferPool.
+ */
+ explicit LocalBufferPool(size_t poolCapacity)
+ : mPoolCapacity(poolCapacity), mUsedSize(0) {
+ }
+
+ /**
+ * Take back the ownership of vec from the destructed VectorBuffer and put
+ * it in front of the pool.
+ */
+ void returnVector(std::vector<uint8_t> &&vec) {
+ Mutex::Autolock lock(mMutex);
+ mPool.push_front(std::move(vec));
+ }
+
+ DISALLOW_EVIL_CONSTRUCTORS(LocalBufferPool);
+};
+
+sp<LinearBlockBuffer> AllocateLinearBuffer(
const std::shared_ptr<C2BlockPool> &pool,
const sp<AMessage> &format,
size_t size,
const C2MemoryUsage &usage) {
std::shared_ptr<C2LinearBlock> block;
- status_t err = pool->fetchLinearBlock(size, usage, &block);
- if (err != OK) {
+ c2_status_t err = pool->fetchLinearBlock(size, usage, &block);
+ if (err != C2_OK) {
return nullptr;
}
return LinearBlockBuffer::Allocate(format, block);
}
+sp<GraphicBlockBuffer> AllocateGraphicBuffer(
+ const std::shared_ptr<C2BlockPool> &pool,
+ const sp<AMessage> &format,
+ uint32_t pixelFormat,
+ const C2MemoryUsage &usage,
+ const std::shared_ptr<LocalBufferPool> &localBufferPool) {
+ int32_t width, height;
+ if (!format->findInt32("width", &width) || !format->findInt32("height", &height)) {
+ ALOGD("format lacks width or height");
+ return nullptr;
+ }
+
+ std::shared_ptr<C2GraphicBlock> block;
+ c2_status_t err = pool->fetchGraphicBlock(
+ width, height, pixelFormat, usage, &block);
+ if (err != C2_OK) {
+ ALOGD("fetch graphic block failed: %d", err);
+ return nullptr;
+ }
+
+ return GraphicBlockBuffer::Allocate(
+ format,
+ block,
+ [localBufferPool](size_t capacity) {
+ return localBufferPool->newBuffer(capacity);
+ });
+}
+
class BuffersArrayImpl;
/**
@@ -386,6 +523,7 @@
class InputBuffersArray : public CCodecBufferChannel::InputBuffers {
public:
InputBuffersArray() = default;
+ ~InputBuffersArray() override = default;
void initialize(
const FlexBuffersImpl &impl,
@@ -435,7 +573,8 @@
// TODO: proper max input size
// TODO: read usage from intf
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- sp<LinearBlockBuffer> newBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage);
+ sp<LinearBlockBuffer> newBuffer = AllocateLinearBuffer(
+ mPool, mFormat, kLinearBufferSize, usage);
if (newBuffer == nullptr) {
return false;
}
@@ -461,7 +600,7 @@
kMinBufferArraySize,
[pool = mPool, format = mFormat] () -> sp<Codec2Buffer> {
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- return allocateLinearBuffer(pool, format, kLinearBufferSize, usage);
+ return AllocateLinearBuffer(pool, format, kLinearBufferSize, usage);
});
return std::move(array);
}
@@ -472,7 +611,54 @@
class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers {
public:
- GraphicInputBuffers() = default;
+ GraphicInputBuffers() : mLocalBufferPool(LocalBufferPool::Create(1920 * 1080 * 16)) {}
+ ~GraphicInputBuffers() override = default;
+
+ bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
+ // TODO: proper max input size
+ // TODO: read usage from intf
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ sp<GraphicBlockBuffer> newBuffer = AllocateGraphicBuffer(
+ mPool, mFormat, HAL_PIXEL_FORMAT_YV12, usage, mLocalBufferPool);
+ if (newBuffer == nullptr) {
+ return false;
+ }
+ *index = mImpl.assignSlot(newBuffer);
+ *buffer = newBuffer;
+ return true;
+ }
+
+ std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
+ return mImpl.releaseSlot(buffer);
+ }
+
+ void flush() override {
+ // This is no-op by default unless we're in array mode where we need to keep
+ // track of the flushed work.
+ }
+
+ std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
+ std::unique_ptr<InputBuffersArray> array(new InputBuffersArray);
+ array->setFormat(mFormat);
+ array->initialize(
+ mImpl,
+ kMinBufferArraySize,
+ [pool = mPool, format = mFormat, lbp = mLocalBufferPool]() -> sp<Codec2Buffer> {
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ return AllocateGraphicBuffer(
+ pool, format, HAL_PIXEL_FORMAT_YV12, usage, lbp);
+ });
+ return std::move(array);
+ }
+
+private:
+ FlexBuffersImpl mImpl;
+ std::shared_ptr<LocalBufferPool> mLocalBufferPool;
+};
+
+class DummyInputBuffers : public CCodecBufferChannel::InputBuffers {
+public:
+ DummyInputBuffers() = default;
bool requestNewBuffer(size_t *, sp<MediaCodecBuffer> *) override {
return false;
@@ -498,7 +684,8 @@
class OutputBuffersArray : public CCodecBufferChannel::OutputBuffers {
public:
- using CCodecBufferChannel::OutputBuffers::OutputBuffers;
+ OutputBuffersArray() = default;
+ ~OutputBuffersArray() override = default;
void initialize(
const FlexBuffersImpl &impl,
@@ -525,12 +712,14 @@
return clientBuffer->canCopy(buffer);
});
if (err != OK) {
- return false;
- }
- if (!c2Buffer->copy(buffer)) {
+ ALOGD("grabBuffer failed: %d", err);
return false;
}
c2Buffer->setFormat(mFormat);
+ if (!c2Buffer->copy(buffer)) {
+ ALOGD("copy buffer failed");
+ return false;
+ }
*clientBuffer = c2Buffer;
return true;
}
@@ -631,8 +820,21 @@
return std::move(array);
}
+ /**
+ * Return an appropriate Codec2Buffer object for the type of buffers.
+ *
+ * \param buffer C2Buffer object to wrap.
+ *
+ * \return appropriate Codec2Buffer object to wrap |buffer|.
+ */
virtual sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) = 0;
+ /**
+ * Return an appropriate Codec2Buffer object for the type of buffers, to be
+ * used as an empty array buffer.
+ *
+ * \return appropriate Codec2Buffer object which can copy() from C2Buffers.
+ */
virtual sp<Codec2Buffer> allocateArrayBuffer() = 0;
private:
@@ -677,6 +879,34 @@
}
};
+class RawGraphicOutputBuffers : public FlexOutputBuffers {
+public:
+ RawGraphicOutputBuffers()
+ : mLocalBufferPool(LocalBufferPool::Create(1920 * 1080 * 16)) {
+ }
+ ~RawGraphicOutputBuffers() override = default;
+
+ sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
+ return ConstGraphicBlockBuffer::Allocate(
+ mFormat,
+ buffer,
+ [lbp = mLocalBufferPool](size_t capacity) {
+ return lbp->newBuffer(capacity);
+ });
+ }
+
+ sp<Codec2Buffer> allocateArrayBuffer() override {
+ return ConstGraphicBlockBuffer::AllocateEmpty(
+ mFormat,
+ [lbp = mLocalBufferPool](size_t capacity) {
+ return lbp->newBuffer(capacity);
+ });
+ }
+
+private:
+ std::shared_ptr<LocalBufferPool> mLocalBufferPool;
+};
+
} // namespace
CCodecBufferChannel::QueueGuard::QueueGuard(
@@ -942,7 +1172,11 @@
bool graphic = (iStreamFormat.value == C2FormatVideo);
if (graphic) {
- buffers->reset(new GraphicInputBuffers);
+ if (mInputSurface) {
+ buffers->reset(new DummyInputBuffers);
+ } else {
+ buffers->reset(new GraphicInputBuffers);
+ }
} else {
buffers->reset(new LinearInputBuffers);
}
@@ -964,11 +1198,21 @@
}
if (outputFormat != nullptr) {
+ bool hasOutputSurface = false;
+ {
+ Mutexed<sp<Surface>>::Locked surface(mSurface);
+ hasOutputSurface = (*surface != nullptr);
+ }
+
Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
bool graphic = (oStreamFormat.value == C2FormatVideo);
if (graphic) {
- buffers->reset(new GraphicOutputBuffers);
+ if (hasOutputSurface) {
+ buffers->reset(new GraphicOutputBuffers);
+ } else {
+ buffers->reset(new RawGraphicOutputBuffers);
+ }
} else {
buffers->reset(new LinearOutputBuffers);
}
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
index 9b39ae9..1fc2c8c 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
@@ -37,15 +37,6 @@
#include "ih264d_defs.h"
-namespace {
-
-template <class T>
-inline int32_t floor32(T arg) {
- return (int32_t) std::llround(std::floor(arg));
-}
-
-} // namespace
-
namespace android {
struct iv_obj_t : public ::iv_obj_t {};
@@ -83,6 +74,20 @@
.build();
}
+void CopyPlane(
+ uint8_t *dst, const C2PlaneInfo &plane,
+ const uint8_t *src, uint32_t width, uint32_t height) {
+ for (uint32_t row = 0; row < height; ++row) {
+ for (uint32_t col = 0; col < width; ++col) {
+ *dst = *src;
+ dst += plane.colInc;
+ ++src;
+ }
+ dst -= plane.colInc * width;
+ dst += plane.rowInc;
+ }
+}
+
void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
uint32_t flags = 0;
if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM)) {
@@ -103,7 +108,7 @@
c2_node_id_t id)
: SimpleC2Component(BuildIntf(name, id)),
mCodecCtx(NULL),
- mFlushOutBuffer(NULL),
+ mOutBuffer(NULL),
mIvColorFormat(IV_YUV_420P),
mChangingResolution(false),
mSignalledError(false),
@@ -164,9 +169,9 @@
}
}
- if (mFlushOutBuffer) {
- free(mFlushOutBuffer);
- mFlushOutBuffer = NULL;
+ if (mOutBuffer) {
+ free(mOutBuffer);
+ mOutBuffer = NULL;
}
return C2_OK;
}
@@ -244,6 +249,16 @@
return UNKNOWN_ERROR;
}
+
+ if (mOutBuffer != NULL) {
+ free(mOutBuffer);
+ }
+ uint32_t bufferSize = mWidth * mHeight * 3 / 2;
+ mOutBuffer = (uint8_t *)memalign(128, bufferSize);
+ if (NULL == mOutBuffer) {
+ ALOGE("Could not allocate output buffer of size %u", bufferSize);
+ return C2_NO_MEMORY;
+ }
return OK;
}
@@ -321,17 +336,6 @@
return UNKNOWN_ERROR;
}
- /* Allocate a picture buffer to flushed data */
- uint32_t displayStride = mWidth;
- uint32_t displayHeight = mHeight;
-
- uint32_t bufferSize = displayStride * displayHeight * 3 / 2;
- mFlushOutBuffer = (uint8_t *)memalign(128, bufferSize);
- if (NULL == mFlushOutBuffer) {
- ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize);
- return C2_NO_MEMORY;
- }
-
return OK;
}
@@ -499,14 +503,29 @@
outBuffer->width(), outBuffer->height(), width, height);
return false;
}
+ ALOGV("width = %u, stride[0] = %u, stride[1] = %u, stride[2] = %u",
+ outBuffer->width(),
+ outBuffer->layout().planes[0].rowInc,
+ outBuffer->layout().planes[1].rowInc,
+ outBuffer->layout().planes[2].rowInc);
+ const C2PlanarLayout &layout = outBuffer->layout();
ps_dec_ip->s_out_buffer.pu1_bufs[0] = outBuffer->data()[0];
+ if (layout.planes[0].rowInc != (int32_t)mWidth || layout.planes[1].colInc != 1) {
+ ps_dec_ip->s_out_buffer.pu1_bufs[0] = mOutBuffer;
+ }
ps_dec_ip->s_out_buffer.pu1_bufs[1] = outBuffer->data()[1];
+ if (layout.planes[1].rowInc != (int32_t)mWidth / 2 || layout.planes[1].colInc != 1) {
+ ps_dec_ip->s_out_buffer.pu1_bufs[1] = mOutBuffer + sizeY;
+ }
ps_dec_ip->s_out_buffer.pu1_bufs[2] = outBuffer->data()[2];
+ if (layout.planes[2].rowInc != (int32_t)mWidth / 2 || layout.planes[2].colInc != 1) {
+ ps_dec_ip->s_out_buffer.pu1_bufs[2] = mOutBuffer + sizeY + sizeUV;
+ }
} else {
- // mFlushOutBuffer always has the right size.
- ps_dec_ip->s_out_buffer.pu1_bufs[0] = mFlushOutBuffer;
- ps_dec_ip->s_out_buffer.pu1_bufs[1] = mFlushOutBuffer + sizeY;
- ps_dec_ip->s_out_buffer.pu1_bufs[2] = mFlushOutBuffer + sizeY + sizeUV;
+ // mOutBuffer always has the right size.
+ ps_dec_ip->s_out_buffer.pu1_bufs[0] = mOutBuffer;
+ ps_dec_ip->s_out_buffer.pu1_bufs[1] = mOutBuffer + sizeY;
+ ps_dec_ip->s_out_buffer.pu1_bufs[2] = mOutBuffer + sizeY + sizeUV;
}
ps_dec_ip->s_out_buffer.u4_num_bufs = 3;
@@ -544,7 +563,8 @@
}
void C2SoftAvcDec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &work) {
- std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(std::move(mAllocatedBlock));
+ std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(mAllocatedBlock);
+ mAllocatedBlock.reset();
auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
uint32_t flags = 0;
if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
@@ -603,34 +623,54 @@
break;
}
(void)ensureDecoderState(pool);
- C2GraphicView output = mAllocatedBlock->map().get();
- if (output.error() != OK) {
- ALOGE("mapped err = %d", output.error());
- }
-
ivd_video_decode_ip_t s_dec_ip;
ivd_video_decode_op_t s_dec_op;
WORD32 timeDelay, timeTaken;
//size_t sizeY, sizeUV;
- if (!setDecodeArgs(&s_dec_ip, &s_dec_op, &input, &output, workIndex, inOffset)) {
- ALOGE("Decoder arg setup failed");
- // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
- mSignalledError = true;
- break;
+ {
+ C2GraphicView output = mAllocatedBlock->map().get();
+ if (output.error() != C2_OK) {
+ ALOGE("mapped err = %d", output.error());
+ work->result = output.error();
+ fillEmptyWork(work);
+ return;
+ }
+ if (!setDecodeArgs(&s_dec_ip, &s_dec_op, &input, &output, workIndex, inOffset)) {
+ ALOGE("Decoder arg setup failed");
+ // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ break;
+ }
+ ALOGV("Decoder arg setup succeeded");
+ // If input dump is enabled, then write to file
+ DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes, mInputOffset);
+
+ GETTIME(&mTimeStart, NULL);
+ /* Compute time elapsed between end of previous decode()
+ * to start of current decode() */
+ TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
+
+ IV_API_CALL_STATUS_T status;
+ status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+ ALOGV("status = %d, error_code = %d", status, (s_dec_op.u4_error_code & 0xFF));
+ if (s_dec_op.u4_output_present) {
+ const C2PlanarLayout &layout = output.layout();
+ if (layout.planes[0].rowInc != (int32_t)mWidth || layout.planes[1].colInc != 1) {
+ CopyPlane(output.data()[0], layout.planes[0], mOutBuffer, mWidth, mHeight);
+ }
+ if (layout.planes[1].rowInc != (int32_t)mWidth / 2 || layout.planes[1].colInc != 1) {
+ CopyPlane(
+ output.data()[1], layout.planes[1],
+ mOutBuffer + (mWidth * mHeight), mWidth / 2, mHeight / 2);
+ }
+ if (layout.planes[2].rowInc != (int32_t)mWidth / 2 || layout.planes[2].colInc != 1) {
+ CopyPlane(
+ output.data()[2], layout.planes[2],
+ mOutBuffer + (mWidth * mHeight * 5 / 4), mWidth / 2, mHeight / 2);
+ }
+ }
}
- ALOGV("Decoder arg setup succeeded");
- // If input dump is enabled, then write to file
- DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes, mInputOffset);
-
- GETTIME(&mTimeStart, NULL);
- /* Compute time elapsed between end of previous decode()
- * to start of current decode() */
- TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
-
- IV_API_CALL_STATUS_T status;
- status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
- ALOGV("status = %d, error_code = %d", status, (s_dec_op.u4_error_code & 0xFF));
bool unsupportedResolution =
(IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & 0xFF));
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
index 6632bf3..d324a0f 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
@@ -189,7 +189,7 @@
struct timeval mTimeEnd; // Time at the end of decode()
// Internal buffer to be used to flush out the buffers from decoder
- uint8_t *mFlushOutBuffer;
+ uint8_t *mOutBuffer;
#ifdef FILE_DUMP_ENABLE
char mInFile[200];
diff --git a/media/libstagefright/include/Codec2Buffer.h b/media/libstagefright/include/Codec2Buffer.h
index 9766b41..eeb889d 100644
--- a/media/libstagefright/include/Codec2Buffer.h
+++ b/media/libstagefright/include/Codec2Buffer.h
@@ -20,6 +20,7 @@
#include <C2Buffer.h>
+#include <media/hardware/VideoAPI.h>
#include <media/MediaCodecBuffer.h>
namespace android {
@@ -109,6 +110,11 @@
public:
/**
* Allocate a new LinearBufferBlock wrapping around C2LinearBlock object.
+ *
+ * \param format mandatory buffer format for MediaCodecBuffer
+ * \param block C2LinearBlock object to wrap around.
+ * \return LinearBlockBuffer object with writable mapping.
+ * nullptr if unsuccessful.
*/
static sp<LinearBlockBuffer> Allocate(
const sp<AMessage> &format, const std::shared_ptr<C2LinearBlock> &block);
@@ -137,6 +143,11 @@
public:
/**
* Allocate a new ConstLinearBlockBuffer wrapping around C2Buffer object.
+ *
+ * \param format mandatory buffer format for MediaCodecBuffer
+ * \param buffer linear C2Buffer object to wrap around.
+ * \return ConstLinearBlockBuffer object with readable mapping.
+ * nullptr if unsuccessful.
*/
static sp<ConstLinearBlockBuffer> Allocate(
const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer);
@@ -156,6 +167,110 @@
std::shared_ptr<C2Buffer> mBufferRef;
};
+/**
+ * MediaCodecBuffer implementation wraps around C2GraphicBlock.
+ *
+ * This object exposes the underlying bits via accessor APIs and "image-data"
+ * metadata, created automatically at allocation time.
+ */
+class GraphicBlockBuffer : public Codec2Buffer {
+public:
+ /**
+ * Allocate a new GraphicBlockBuffer wrapping around C2GraphicBlock object.
+ * If |block| is not in good color formats, it allocates YV12 local buffer
+ * and copies the content over at asC2Buffer().
+ *
+ * \param format mandatory buffer format for MediaCodecBuffer
+ * \param block C2GraphicBlock object to wrap around.
+ * \param alloc a function to allocate backing ABuffer if needed.
+ * \return GraphicBlockBuffer object with writable mapping.
+ * nullptr if unsuccessful.
+ */
+ static sp<GraphicBlockBuffer> Allocate(
+ const sp<AMessage> &format,
+ const std::shared_ptr<C2GraphicBlock> &block,
+ std::function<sp<ABuffer>(size_t)> alloc);
+
+ std::shared_ptr<C2Buffer> asC2Buffer() override;
+
+ virtual ~GraphicBlockBuffer() = default;
+
+private:
+ GraphicBlockBuffer(
+ const sp<AMessage> &format,
+ const sp<ABuffer> &buffer,
+ C2GraphicView &&view,
+ const std::shared_ptr<C2GraphicBlock> &block,
+ const sp<ABuffer> &imageData,
+ bool wrapped);
+ GraphicBlockBuffer() = delete;
+
+ inline MediaImage2 *imageData() { return (MediaImage2 *)mImageData->data(); }
+
+ C2GraphicView mView;
+ std::shared_ptr<C2GraphicBlock> mBlock;
+ sp<ABuffer> mImageData;
+ const bool mWrapped;
+};
+
+/**
+ * MediaCodecBuffer implementation wraps around graphic C2Buffer object.
+ *
+ * This object exposes the underlying bits via accessor APIs and "image-data"
+ * metadata, created automatically at allocation time.
+ */
+class ConstGraphicBlockBuffer : public Codec2Buffer {
+public:
+ /**
+ * Allocate a new ConstGraphicBlockBuffer wrapping around C2Buffer object.
+ * If |buffer| is not in good color formats, it allocates YV12 local buffer
+ * and copies the content of |buffer| over to expose.
+ *
+ * \param format mandatory buffer format for MediaCodecBuffer
+ * \param buffer graphic C2Buffer object to wrap around.
+ * \param alloc a function to allocate backing ABuffer if needed.
+ * \return ConstGraphicBlockBuffer object with readable mapping.
+ * nullptr if unsuccessful.
+ */
+ static sp<ConstGraphicBlockBuffer> Allocate(
+ const sp<AMessage> &format,
+ const std::shared_ptr<C2Buffer> &buffer,
+ std::function<sp<ABuffer>(size_t)> alloc);
+
+ /**
+ * Allocate a new ConstGraphicBlockBuffer which allocates YV12 local buffer
+ * and copies the content of |buffer| over to expose.
+ *
+ * \param format mandatory buffer format for MediaCodecBuffer
+ * \param alloc a function to allocate backing ABuffer if needed.
+ * \return ConstGraphicBlockBuffer object with no wrapping buffer.
+ */
+ static sp<ConstGraphicBlockBuffer> AllocateEmpty(
+ const sp<AMessage> &format,
+ std::function<sp<ABuffer>(size_t)> alloc);
+
+ std::shared_ptr<C2Buffer> asC2Buffer() override;
+ bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const override;
+ bool copy(const std::shared_ptr<C2Buffer> &buffer) override;
+
+ virtual ~ConstGraphicBlockBuffer() = default;
+
+private:
+ ConstGraphicBlockBuffer(
+ const sp<AMessage> &format,
+ const sp<ABuffer> &aBuffer,
+ std::unique_ptr<const C2GraphicView> &&view,
+ const std::shared_ptr<C2Buffer> &buffer,
+ const sp<ABuffer> &imageData,
+ bool wrapped);
+ ConstGraphicBlockBuffer() = delete;
+
+ sp<ABuffer> mImageData;
+ std::unique_ptr<const C2GraphicView> mView;
+ std::shared_ptr<C2Buffer> mBufferRef;
+ const bool mWrapped;
+};
+
} // namespace android
#endif // CODEC2_BUFFER_H_