|  | /* | 
|  | * Copyright 2018, The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | //#define LOG_NDEBUG 0 | 
|  | #define LOG_TAG "Codec2Buffer" | 
|  | #define ATRACE_TAG  ATRACE_TAG_VIDEO | 
|  | #include <utils/Log.h> | 
|  | #include <utils/Trace.h> | 
|  |  | 
|  | #include <aidl/android/hardware/graphics/common/Cta861_3.h> | 
|  | #include <aidl/android/hardware/graphics/common/Smpte2086.h> | 
|  | #include <android-base/no_destructor.h> | 
|  | #include <android-base/properties.h> | 
|  | #include <android/hardware/cas/native/1.0/types.h> | 
|  | #include <android/hardware/drm/1.0/types.h> | 
|  | #include <hidlmemory/FrameworkUtils.h> | 
|  | #include <media/hardware/HardwareAPI.h> | 
|  | #include <media/stagefright/CodecBase.h> | 
|  | #include <media/stagefright/MediaCodecConstants.h> | 
|  | #include <media/stagefright/foundation/ABuffer.h> | 
|  | #include <media/stagefright/foundation/AMessage.h> | 
|  | #include <media/stagefright/foundation/AUtils.h> | 
|  | #include <mediadrm/ICrypto.h> | 
|  | #include <nativebase/nativebase.h> | 
|  | #include <ui/GraphicBufferMapper.h> | 
|  | #include <ui/Fence.h> | 
|  |  | 
|  | #include <C2AllocatorGralloc.h> | 
|  | #include <C2BlockInternal.h> | 
|  | #include <C2Debug.h> | 
|  |  | 
|  | #include "Codec2Buffer.h" | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | // Codec2Buffer | 
|  |  | 
|  | bool Codec2Buffer::canCopyLinear(const std::shared_ptr<C2Buffer> &buffer) const { | 
|  | 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; | 
|  | } | 
|  | if (buffer->data().linearBlocks().size() == 0u) { | 
|  | // Nothing to copy, so we can copy by doing nothing. | 
|  | return true; | 
|  | } else if (buffer->data().linearBlocks().size() > 1u) { | 
|  | // We don't know how to copy more than one blocks. | 
|  | return false; | 
|  | } | 
|  | if (buffer->data().linearBlocks()[0].size() > capacity()) { | 
|  | // It won't fit. | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Codec2Buffer::copyLinear(const std::shared_ptr<C2Buffer> &buffer) { | 
|  | // We assume that all canCopyLinear() checks passed. | 
|  | if (!buffer || buffer->data().linearBlocks().size() == 0u | 
|  | || buffer->data().linearBlocks()[0].size() == 0u) { | 
|  | setRange(0, 0); | 
|  | return true; | 
|  | } | 
|  | C2ReadView view = buffer->data().linearBlocks()[0].map().get(); | 
|  | if (view.error() != C2_OK) { | 
|  | ALOGD("Error while mapping: %d", view.error()); | 
|  | return false; | 
|  | } | 
|  | if (view.capacity() > capacity()) { | 
|  | ALOGD("C2ConstLinearBlock lied --- it actually doesn't fit: view(%u) > this(%zu)", | 
|  | view.capacity(), capacity()); | 
|  | return false; | 
|  | } | 
|  | memcpy(base(), view.data(), view.capacity()); | 
|  | setRange(0, view.capacity()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void Codec2Buffer::setImageData(const sp<ABuffer> &imageData) { | 
|  | mImageData = imageData; | 
|  | } | 
|  |  | 
|  | // LocalLinearBuffer | 
|  |  | 
|  | bool LocalLinearBuffer::canCopy(const std::shared_ptr<C2Buffer> &buffer) const { | 
|  | return canCopyLinear(buffer); | 
|  | } | 
|  |  | 
|  | bool LocalLinearBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) { | 
|  | return copyLinear(buffer); | 
|  | } | 
|  |  | 
|  | // DummyContainerBuffer | 
|  |  | 
|  | static uint8_t sDummyByte[1] = { 0 }; | 
|  |  | 
|  | DummyContainerBuffer::DummyContainerBuffer( | 
|  | const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer) | 
|  | : Codec2Buffer(format, new ABuffer(sDummyByte, 1)), | 
|  | mBufferRef(buffer) { | 
|  | setRange(0, buffer ? 1 : 0); | 
|  | } | 
|  |  | 
|  | std::shared_ptr<C2Buffer> DummyContainerBuffer::asC2Buffer() { | 
|  | return mBufferRef; | 
|  | } | 
|  |  | 
|  | void DummyContainerBuffer::clearC2BufferRefs() { | 
|  | mBufferRef.reset(); | 
|  | } | 
|  |  | 
|  | bool DummyContainerBuffer::canCopy(const std::shared_ptr<C2Buffer> &) const { | 
|  | return !mBufferRef; | 
|  | } | 
|  |  | 
|  | bool DummyContainerBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) { | 
|  | mBufferRef = buffer; | 
|  | setRange(0, mBufferRef ? 1 : 0); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // LinearBlockBuffer | 
|  |  | 
|  | // static | 
|  | sp<LinearBlockBuffer> LinearBlockBuffer::Allocate( | 
|  | const sp<AMessage> &format, const std::shared_ptr<C2LinearBlock> &block) { | 
|  | C2WriteView writeView(block->map().get()); | 
|  | if (writeView.error() != C2_OK) { | 
|  | return nullptr; | 
|  | } | 
|  | return new LinearBlockBuffer(format, std::move(writeView), block); | 
|  | } | 
|  |  | 
|  | std::shared_ptr<C2Buffer> LinearBlockBuffer::asC2Buffer() { | 
|  | return C2Buffer::CreateLinearBuffer(mBlock->share(offset(), size(), C2Fence())); | 
|  | } | 
|  |  | 
|  | bool LinearBlockBuffer::canCopy(const std::shared_ptr<C2Buffer> &buffer) const { | 
|  | return canCopyLinear(buffer); | 
|  | } | 
|  |  | 
|  | bool LinearBlockBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) { | 
|  | return copyLinear(buffer); | 
|  | } | 
|  |  | 
|  | LinearBlockBuffer::LinearBlockBuffer( | 
|  | const sp<AMessage> &format, | 
|  | C2WriteView&& writeView, | 
|  | const std::shared_ptr<C2LinearBlock> &block) | 
|  | : Codec2Buffer(format, new ABuffer(writeView.data(), writeView.size())), | 
|  | mWriteView(writeView), | 
|  | mBlock(block) { | 
|  | } | 
|  |  | 
|  | // ConstLinearBlockBuffer | 
|  |  | 
|  | // static | 
|  | sp<ConstLinearBlockBuffer> ConstLinearBlockBuffer::Allocate( | 
|  | const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer) { | 
|  | if (!buffer | 
|  | || buffer->data().type() != C2BufferData::LINEAR | 
|  | || buffer->data().linearBlocks().size() != 1u) { | 
|  | return nullptr; | 
|  | } | 
|  | C2ReadView readView(buffer->data().linearBlocks()[0].map().get()); | 
|  | if (readView.error() != C2_OK) { | 
|  | return nullptr; | 
|  | } | 
|  | return new ConstLinearBlockBuffer(format, std::move(readView), buffer); | 
|  | } | 
|  |  | 
|  | ConstLinearBlockBuffer::ConstLinearBlockBuffer( | 
|  | const sp<AMessage> &format, | 
|  | C2ReadView&& readView, | 
|  | const std::shared_ptr<C2Buffer> &buffer) | 
|  | : Codec2Buffer(format, new ABuffer( | 
|  | // NOTE: ABuffer only takes non-const pointer but this data is | 
|  | //       supposed to be read-only. | 
|  | const_cast<uint8_t *>(readView.data()), readView.capacity())), | 
|  | mReadView(readView), | 
|  | mBufferRef(buffer) { | 
|  | } | 
|  |  | 
|  | std::shared_ptr<C2Buffer> ConstLinearBlockBuffer::asC2Buffer() { | 
|  | return mBufferRef; | 
|  | } | 
|  |  | 
|  | void ConstLinearBlockBuffer::clearC2BufferRefs() { | 
|  | mBufferRef.reset(); | 
|  | } | 
|  |  | 
|  | // GraphicView2MediaImageConverter | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class GraphicView2MediaImageConverter { | 
|  | public: | 
|  | /** | 
|  | * Creates a C2GraphicView <=> MediaImage converter | 
|  | * | 
|  | * \param view C2GraphicView object | 
|  | * \param format buffer format | 
|  | * \param copy whether the converter is used for copy or not | 
|  | */ | 
|  | GraphicView2MediaImageConverter( | 
|  | const C2GraphicView &view, const sp<AMessage> &format, bool copy) | 
|  | : mInitCheck(NO_INIT), | 
|  | mView(view), | 
|  | mWidth(view.width()), | 
|  | mHeight(view.height()), | 
|  | mAllocatedDepth(0), | 
|  | mBackBufferSize(0), | 
|  | mMediaImage(new ABuffer(sizeof(MediaImage2))) { | 
|  | ATRACE_CALL(); | 
|  | if (!format->findInt32(KEY_COLOR_FORMAT, &mClientColorFormat)) { | 
|  | mClientColorFormat = COLOR_FormatYUV420Flexible; | 
|  | } | 
|  | if (!format->findInt32("android._color-format", &mComponentColorFormat)) { | 
|  | mComponentColorFormat = COLOR_FormatYUV420Flexible; | 
|  | } | 
|  | 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; | 
|  | } | 
|  | memset(mediaImage, 0, sizeof(*mediaImage)); | 
|  | mAllocatedDepth = layout.planes[0].allocatedDepth; | 
|  | uint32_t bitDepth = layout.planes[0].bitDepth; | 
|  |  | 
|  | // align width and height to support subsampling cleanly | 
|  | uint32_t stride = align(view.crop().width, 2) * divUp(layout.planes[0].allocatedDepth, 8u); | 
|  | uint32_t vStride = align(view.crop().height, 2); | 
|  |  | 
|  | bool tryWrapping = !copy; | 
|  |  | 
|  | switch (layout.type) { | 
|  | case C2PlanarLayout::TYPE_YUV: { | 
|  | mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_YUV; | 
|  | if (layout.numPlanes != 3) { | 
|  | ALOGD("Converter: %d planes for YUV layout", layout.numPlanes); | 
|  | mInitCheck = BAD_VALUE; | 
|  | return; | 
|  | } | 
|  | std::optional<int> clientBitDepth = {}; | 
|  | switch (mClientColorFormat) { | 
|  | case COLOR_FormatYUVP010: | 
|  | clientBitDepth = 10; | 
|  | break; | 
|  | case COLOR_FormatYUV411PackedPlanar: | 
|  | case COLOR_FormatYUV411Planar: | 
|  | case COLOR_FormatYUV420Flexible: | 
|  | case COLOR_FormatYUV420PackedPlanar: | 
|  | case COLOR_FormatYUV420PackedSemiPlanar: | 
|  | case COLOR_FormatYUV420Planar: | 
|  | case COLOR_FormatYUV420SemiPlanar: | 
|  | case COLOR_FormatYUV422Flexible: | 
|  | case COLOR_FormatYUV422PackedPlanar: | 
|  | case COLOR_FormatYUV422PackedSemiPlanar: | 
|  | case COLOR_FormatYUV422Planar: | 
|  | case COLOR_FormatYUV422SemiPlanar: | 
|  | case COLOR_FormatYUV444Flexible: | 
|  | case COLOR_FormatYUV444Interleaved: | 
|  | clientBitDepth = 8; | 
|  | break; | 
|  | default: | 
|  | // no-op; used with optional | 
|  | break; | 
|  |  | 
|  | } | 
|  | // conversion fails if client bit-depth and the component bit-depth differs | 
|  | if ((clientBitDepth) && (bitDepth != clientBitDepth.value())) { | 
|  | ALOGD("Bit depth of client: %d and component: %d differs", | 
|  | *clientBitDepth, bitDepth); | 
|  | mInitCheck = BAD_VALUE; | 
|  | return; | 
|  | } | 
|  | C2PlaneInfo yPlane = layout.planes[C2PlanarLayout::PLANE_Y]; | 
|  | C2PlaneInfo uPlane = layout.planes[C2PlanarLayout::PLANE_U]; | 
|  | C2PlaneInfo vPlane = layout.planes[C2PlanarLayout::PLANE_V]; | 
|  | if (yPlane.channel != C2PlaneInfo::CHANNEL_Y | 
|  | || uPlane.channel != C2PlaneInfo::CHANNEL_CB | 
|  | || vPlane.channel != C2PlaneInfo::CHANNEL_CR) { | 
|  | ALOGD("Converter: not YUV layout"); | 
|  | mInitCheck = BAD_VALUE; | 
|  | return; | 
|  | } | 
|  | bool yuv420888 = yPlane.rowSampling == 1 && yPlane.colSampling == 1 | 
|  | && uPlane.rowSampling == 2 && uPlane.colSampling == 2 | 
|  | && vPlane.rowSampling == 2 && vPlane.colSampling == 2; | 
|  | if (yuv420888) { | 
|  | for (uint32_t i = 0; i < 3; ++i) { | 
|  | const C2PlaneInfo &plane = layout.planes[i]; | 
|  | if (plane.allocatedDepth != 8 || plane.bitDepth != 8) { | 
|  | yuv420888 = false; | 
|  | break; | 
|  | } | 
|  | } | 
|  | yuv420888 = yuv420888 && yPlane.colInc == 1 && uPlane.rowInc == vPlane.rowInc; | 
|  | } | 
|  | int32_t copyFormat = mClientColorFormat; | 
|  | if (yuv420888 && mClientColorFormat == COLOR_FormatYUV420Flexible) { | 
|  | if (uPlane.colInc == 2 && vPlane.colInc == 2 | 
|  | && yPlane.rowInc == uPlane.rowInc) { | 
|  | copyFormat = COLOR_FormatYUV420PackedSemiPlanar; | 
|  | } else if (uPlane.colInc == 1 && vPlane.colInc == 1 | 
|  | && yPlane.rowInc == uPlane.rowInc * 2) { | 
|  | copyFormat = COLOR_FormatYUV420PackedPlanar; | 
|  | } | 
|  | } | 
|  | ALOGV("client_fmt=0x%x y:{colInc=%d rowInc=%d} u:{colInc=%d rowInc=%d} " | 
|  | "v:{colInc=%d rowInc=%d}", | 
|  | mClientColorFormat, | 
|  | yPlane.colInc, yPlane.rowInc, | 
|  | uPlane.colInc, uPlane.rowInc, | 
|  | vPlane.colInc, vPlane.rowInc); | 
|  | switch (copyFormat) { | 
|  | case COLOR_FormatYUV420Flexible: | 
|  | case COLOR_FormatYUV420Planar: | 
|  | case COLOR_FormatYUV420PackedPlanar: | 
|  | mediaImage->mPlane[mediaImage->Y].mOffset = 0; | 
|  | mediaImage->mPlane[mediaImage->Y].mColInc = 1; | 
|  | mediaImage->mPlane[mediaImage->Y].mRowInc = stride; | 
|  | mediaImage->mPlane[mediaImage->Y].mHorizSubsampling = 1; | 
|  | mediaImage->mPlane[mediaImage->Y].mVertSubsampling = 1; | 
|  |  | 
|  | mediaImage->mPlane[mediaImage->U].mOffset = stride * vStride; | 
|  | mediaImage->mPlane[mediaImage->U].mColInc = 1; | 
|  | mediaImage->mPlane[mediaImage->U].mRowInc = stride / 2; | 
|  | mediaImage->mPlane[mediaImage->U].mHorizSubsampling = 2; | 
|  | mediaImage->mPlane[mediaImage->U].mVertSubsampling = 2; | 
|  |  | 
|  | mediaImage->mPlane[mediaImage->V].mOffset = stride * vStride * 5 / 4; | 
|  | mediaImage->mPlane[mediaImage->V].mColInc = 1; | 
|  | mediaImage->mPlane[mediaImage->V].mRowInc = stride / 2; | 
|  | mediaImage->mPlane[mediaImage->V].mHorizSubsampling = 2; | 
|  | mediaImage->mPlane[mediaImage->V].mVertSubsampling = 2; | 
|  |  | 
|  | if (tryWrapping && mClientColorFormat != COLOR_FormatYUV420Flexible) { | 
|  | tryWrapping = yuv420888 && uPlane.colInc == 1 && vPlane.colInc == 1 | 
|  | && yPlane.rowInc == uPlane.rowInc * 2 | 
|  | && view.data()[0] < view.data()[1] | 
|  | && view.data()[1] < view.data()[2]; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case COLOR_FormatYUV420SemiPlanar: | 
|  | case COLOR_FormatYUV420PackedSemiPlanar: | 
|  | mediaImage->mPlane[mediaImage->Y].mOffset = 0; | 
|  | mediaImage->mPlane[mediaImage->Y].mColInc = 1; | 
|  | mediaImage->mPlane[mediaImage->Y].mRowInc = stride; | 
|  | mediaImage->mPlane[mediaImage->Y].mHorizSubsampling = 1; | 
|  | mediaImage->mPlane[mediaImage->Y].mVertSubsampling = 1; | 
|  |  | 
|  | mediaImage->mPlane[mediaImage->U].mOffset = stride * vStride; | 
|  | mediaImage->mPlane[mediaImage->U].mColInc = 2; | 
|  | mediaImage->mPlane[mediaImage->U].mRowInc = stride; | 
|  | mediaImage->mPlane[mediaImage->U].mHorizSubsampling = 2; | 
|  | mediaImage->mPlane[mediaImage->U].mVertSubsampling = 2; | 
|  |  | 
|  | mediaImage->mPlane[mediaImage->V].mOffset = stride * vStride + 1; | 
|  | mediaImage->mPlane[mediaImage->V].mColInc = 2; | 
|  | mediaImage->mPlane[mediaImage->V].mRowInc = stride; | 
|  | mediaImage->mPlane[mediaImage->V].mHorizSubsampling = 2; | 
|  | mediaImage->mPlane[mediaImage->V].mVertSubsampling = 2; | 
|  |  | 
|  | if (tryWrapping && mClientColorFormat != COLOR_FormatYUV420Flexible) { | 
|  | tryWrapping = yuv420888 && uPlane.colInc == 2 && vPlane.colInc == 2 | 
|  | && yPlane.rowInc == uPlane.rowInc | 
|  | && view.data()[0] < view.data()[1] | 
|  | && view.data()[1] < view.data()[2]; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case COLOR_FormatYUVP010: | 
|  | // stride is in bytes | 
|  | mediaImage->mPlane[mediaImage->Y].mOffset = 0; | 
|  | mediaImage->mPlane[mediaImage->Y].mColInc = 2; | 
|  | mediaImage->mPlane[mediaImage->Y].mRowInc = stride; | 
|  | mediaImage->mPlane[mediaImage->Y].mHorizSubsampling = 1; | 
|  | mediaImage->mPlane[mediaImage->Y].mVertSubsampling = 1; | 
|  |  | 
|  | mediaImage->mPlane[mediaImage->U].mOffset = stride * vStride; | 
|  | mediaImage->mPlane[mediaImage->U].mColInc = 4; | 
|  | mediaImage->mPlane[mediaImage->U].mRowInc = stride; | 
|  | mediaImage->mPlane[mediaImage->U].mHorizSubsampling = 2; | 
|  | mediaImage->mPlane[mediaImage->U].mVertSubsampling = 2; | 
|  |  | 
|  | mediaImage->mPlane[mediaImage->V].mOffset = stride * vStride + 2; | 
|  | mediaImage->mPlane[mediaImage->V].mColInc = 4; | 
|  | mediaImage->mPlane[mediaImage->V].mRowInc = stride; | 
|  | mediaImage->mPlane[mediaImage->V].mHorizSubsampling = 2; | 
|  | mediaImage->mPlane[mediaImage->V].mVertSubsampling = 2; | 
|  | if (tryWrapping) { | 
|  | tryWrapping = yPlane.allocatedDepth == 16 | 
|  | && uPlane.allocatedDepth == 16 | 
|  | && vPlane.allocatedDepth == 16 | 
|  | && yPlane.bitDepth == 10 | 
|  | && uPlane.bitDepth == 10 | 
|  | && vPlane.bitDepth == 10 | 
|  | && yPlane.rightShift == 6 | 
|  | && uPlane.rightShift == 6 | 
|  | && vPlane.rightShift == 6 | 
|  | && yPlane.rowSampling == 1 && yPlane.colSampling == 1 | 
|  | && uPlane.rowSampling == 2 && uPlane.colSampling == 2 | 
|  | && vPlane.rowSampling == 2 && vPlane.colSampling == 2 | 
|  | && yPlane.colInc == 2 | 
|  | && uPlane.colInc == 4 | 
|  | && vPlane.colInc == 4 | 
|  | && yPlane.rowInc == uPlane.rowInc | 
|  | && yPlane.rowInc == vPlane.rowInc; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: { | 
|  | // default to fully planar format --- this will be overridden if wrapping | 
|  | // TODO: keep interleaved format | 
|  | int32_t colInc = divUp(mAllocatedDepth, 8u); | 
|  | int32_t rowInc = stride * colInc / yPlane.colSampling; | 
|  | mediaImage->mPlane[mediaImage->Y].mOffset = 0; | 
|  | mediaImage->mPlane[mediaImage->Y].mColInc = colInc; | 
|  | mediaImage->mPlane[mediaImage->Y].mRowInc = rowInc; | 
|  | mediaImage->mPlane[mediaImage->Y].mHorizSubsampling = yPlane.colSampling; | 
|  | mediaImage->mPlane[mediaImage->Y].mVertSubsampling = yPlane.rowSampling; | 
|  | int32_t offset = rowInc * vStride / yPlane.rowSampling; | 
|  |  | 
|  | rowInc = stride * colInc / uPlane.colSampling; | 
|  | mediaImage->mPlane[mediaImage->U].mOffset = offset; | 
|  | mediaImage->mPlane[mediaImage->U].mColInc = colInc; | 
|  | mediaImage->mPlane[mediaImage->U].mRowInc = rowInc; | 
|  | mediaImage->mPlane[mediaImage->U].mHorizSubsampling = uPlane.colSampling; | 
|  | mediaImage->mPlane[mediaImage->U].mVertSubsampling = uPlane.rowSampling; | 
|  | offset += rowInc * vStride / uPlane.rowSampling; | 
|  |  | 
|  | rowInc = stride * colInc / vPlane.colSampling; | 
|  | mediaImage->mPlane[mediaImage->V].mOffset = offset; | 
|  | mediaImage->mPlane[mediaImage->V].mColInc = colInc; | 
|  | mediaImage->mPlane[mediaImage->V].mRowInc = rowInc; | 
|  | mediaImage->mPlane[mediaImage->V].mHorizSubsampling = vPlane.colSampling; | 
|  | mediaImage->mPlane[mediaImage->V].mVertSubsampling = vPlane.rowSampling; | 
|  | break; | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case C2PlanarLayout::TYPE_YUVA: | 
|  | ALOGD("Converter: unrecognized color format " | 
|  | "(client %d component %d) for YUVA layout", | 
|  | mClientColorFormat, mComponentColorFormat); | 
|  | mInitCheck = NO_INIT; | 
|  | return; | 
|  | case C2PlanarLayout::TYPE_RGB: | 
|  | mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_RGB; | 
|  | // TODO: support MediaImage layout | 
|  | switch (mClientColorFormat) { | 
|  | case COLOR_FormatSurface: | 
|  | case COLOR_FormatRGBFlexible: | 
|  | case COLOR_Format24bitBGR888: | 
|  | case COLOR_Format24bitRGB888: | 
|  | ALOGD("Converter: accept color format " | 
|  | "(client %d component %d) for RGB layout", | 
|  | mClientColorFormat, mComponentColorFormat); | 
|  | break; | 
|  | default: | 
|  | ALOGD("Converter: unrecognized color format " | 
|  | "(client %d component %d) for RGB layout", | 
|  | mClientColorFormat, mComponentColorFormat); | 
|  | mInitCheck = BAD_VALUE; | 
|  | return; | 
|  | } | 
|  | if (layout.numPlanes != 3) { | 
|  | ALOGD("Converter: %d planes for RGB layout", layout.numPlanes); | 
|  | mInitCheck = BAD_VALUE; | 
|  | return; | 
|  | } | 
|  | break; | 
|  | case C2PlanarLayout::TYPE_RGBA: | 
|  | mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_RGBA; | 
|  | // TODO: support MediaImage layout | 
|  | switch (mClientColorFormat) { | 
|  | case COLOR_FormatSurface: | 
|  | case COLOR_FormatRGBAFlexible: | 
|  | case COLOR_Format32bitABGR8888: | 
|  | case COLOR_Format32bitARGB8888: | 
|  | case COLOR_Format32bitBGRA8888: | 
|  | ALOGD("Converter: accept color format " | 
|  | "(client %d component %d) for RGBA layout", | 
|  | mClientColorFormat, mComponentColorFormat); | 
|  | break; | 
|  | default: | 
|  | ALOGD("Converter: unrecognized color format " | 
|  | "(client %d component %d) for RGBA layout", | 
|  | mClientColorFormat, mComponentColorFormat); | 
|  | mInitCheck = BAD_VALUE; | 
|  | return; | 
|  | } | 
|  | if (layout.numPlanes != 4) { | 
|  | ALOGD("Converter: %d planes for RGBA layout", layout.numPlanes); | 
|  | mInitCheck = BAD_VALUE; | 
|  | return; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_UNKNOWN; | 
|  | if (layout.numPlanes == 1) { | 
|  | const C2PlaneInfo &plane = layout.planes[0]; | 
|  | if (plane.colInc < 0 || plane.rowInc < 0) { | 
|  | // Copy-only if we have negative colInc/rowInc | 
|  | tryWrapping = false; | 
|  | } | 
|  | mediaImage->mPlane[0].mOffset = 0; | 
|  | mediaImage->mPlane[0].mColInc = std::abs(plane.colInc); | 
|  | mediaImage->mPlane[0].mRowInc = std::abs(plane.rowInc); | 
|  | mediaImage->mPlane[0].mHorizSubsampling = plane.colSampling; | 
|  | mediaImage->mPlane[0].mVertSubsampling = plane.rowSampling; | 
|  | } else { | 
|  | ALOGD("Converter: unrecognized layout: color format (client %d component %d)", | 
|  | mClientColorFormat, mComponentColorFormat); | 
|  | mInitCheck = NO_INIT; | 
|  | return; | 
|  | } | 
|  | break; | 
|  | } | 
|  | if (tryWrapping) { | 
|  | // try to map directly. check if the planes are near one another | 
|  | 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]; | 
|  | int64_t planeStride = std::abs(plane.rowInc / plane.colInc); | 
|  | ssize_t minOffset = plane.minOffset( | 
|  | mWidth / plane.colSampling, mHeight / plane.rowSampling); | 
|  | ssize_t maxOffset = plane.maxOffset( | 
|  | mWidth / plane.colSampling, mHeight / plane.rowSampling); | 
|  | if (minPtr > mView.data()[i] + minOffset) { | 
|  | minPtr = mView.data()[i] + minOffset; | 
|  | } | 
|  | if (maxPtr < mView.data()[i] + maxOffset) { | 
|  | maxPtr = mView.data()[i] + maxOffset; | 
|  | } | 
|  | planeSize += planeStride * divUp(mAllocatedDepth, 8u) | 
|  | * align(mHeight, 64) / plane.rowSampling; | 
|  | } | 
|  |  | 
|  | if (minPtr == mView.data()[0] && (maxPtr - minPtr) <= planeSize) { | 
|  | // FIXME: this is risky as reading/writing data out of bound results | 
|  | //        in an undefined behavior, but gralloc does assume a | 
|  | //        contiguous mapping | 
|  | 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; | 
|  | } | 
|  | mWrapped = new ABuffer(const_cast<uint8_t *>(minPtr), maxPtr - minPtr); | 
|  | ALOGV("Converter: wrapped (capacity=%zu)", mWrapped->capacity()); | 
|  | } | 
|  | } | 
|  | mediaImage->mNumPlanes = layout.numPlanes; | 
|  | mediaImage->mWidth = view.crop().width; | 
|  | mediaImage->mHeight = view.crop().height; | 
|  | 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.allocatedDepth < plane.bitDepth | 
|  | || plane.rightShift != plane.allocatedDepth - plane.bitDepth) { | 
|  | ALOGD("rightShift value of %u unsupported", plane.rightShift); | 
|  | mInitCheck = BAD_VALUE; | 
|  | return; | 
|  | } | 
|  | if (plane.allocatedDepth > 8 && plane.endianness != C2PlaneInfo::NATIVE) { | 
|  | ALOGD("endianness value of %u unsupported", plane.endianness); | 
|  | mInitCheck = BAD_VALUE; | 
|  | return; | 
|  | } | 
|  | if (plane.allocatedDepth != mAllocatedDepth || plane.bitDepth != bitDepth) { | 
|  | ALOGD("different allocatedDepth/bitDepth per plane unsupported"); | 
|  | mInitCheck = BAD_VALUE; | 
|  | return; | 
|  | } | 
|  | // stride is in bytes | 
|  | bufferSize += stride * vStride / plane.rowSampling / plane.colSampling; | 
|  | } | 
|  |  | 
|  | mBackBufferSize = bufferSize; | 
|  | mInitCheck = OK; | 
|  | } | 
|  |  | 
|  | status_t initCheck() const { return mInitCheck; } | 
|  |  | 
|  | uint32_t backBufferSize() const { return mBackBufferSize; } | 
|  |  | 
|  | /** | 
|  | * Wrap C2GraphicView using a MediaImage2. Note that if not wrapped, the content is not mapped | 
|  | * in this function --- the caller should use CopyGraphicView2MediaImage() function to copy the | 
|  | * data into a backing buffer explicitly. | 
|  | * | 
|  | * \return media buffer. This is null if wrapping failed. | 
|  | */ | 
|  | sp<ABuffer> wrap() const { | 
|  | if (mBackBuffer == nullptr) { | 
|  | return mWrapped; | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | bool setBackBuffer(const sp<ABuffer> &backBuffer) { | 
|  | if (backBuffer == nullptr) { | 
|  | return false; | 
|  | } | 
|  | if (backBuffer->capacity() < mBackBufferSize) { | 
|  | return false; | 
|  | } | 
|  | backBuffer->setRange(0, mBackBufferSize); | 
|  | mBackBuffer = backBuffer; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Copy C2GraphicView to MediaImage2. | 
|  | */ | 
|  | status_t copyToMediaImage() { | 
|  | ATRACE_CALL(); | 
|  | if (mInitCheck != OK) { | 
|  | return mInitCheck; | 
|  | } | 
|  | return ImageCopy(mBackBuffer->base(), getMediaImage(), mView); | 
|  | } | 
|  |  | 
|  | const sp<ABuffer> &imageData() const { return mMediaImage; } | 
|  |  | 
|  | private: | 
|  | status_t mInitCheck; | 
|  |  | 
|  | const C2GraphicView mView; | 
|  | uint32_t mWidth; | 
|  | uint32_t mHeight; | 
|  | int32_t mClientColorFormat;  ///< SDK color format for MediaImage | 
|  | int32_t mComponentColorFormat;  ///< SDK color format from component | 
|  | sp<ABuffer> mWrapped;  ///< wrapped buffer (if we can map C2Buffer to an ABuffer) | 
|  | uint32_t mAllocatedDepth; | 
|  | uint32_t mBackBufferSize; | 
|  | sp<ABuffer> mMediaImage; | 
|  | std::function<sp<ABuffer>(size_t)> mAlloc; | 
|  |  | 
|  | sp<ABuffer> mBackBuffer;    ///< backing buffer if we have to copy C2Buffer <=> ABuffer | 
|  |  | 
|  | 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) { | 
|  | ATRACE_BEGIN("GraphicBlockBuffer::Allocate block->map()"); | 
|  | C2GraphicView view(block->map().get()); | 
|  | ATRACE_END(); | 
|  | if (view.error() != C2_OK) { | 
|  | ALOGD("C2GraphicBlock::map failed: %d", view.error()); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | GraphicView2MediaImageConverter converter(view, format, false /* copy */); | 
|  | 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), | 
|  | mWrapped(wrapped) { | 
|  | setImageData(imageData); | 
|  | } | 
|  |  | 
|  | std::shared_ptr<C2Buffer> GraphicBlockBuffer::asC2Buffer() { | 
|  | ATRACE_CALL(); | 
|  | uint32_t width = mView.width(); | 
|  | uint32_t height = mView.height(); | 
|  | if (!mWrapped) { | 
|  | (void)ImageCopy(mView, base(), imageData()); | 
|  | } | 
|  | return C2Buffer::CreateGraphicBuffer( | 
|  | mBlock->share(C2Rect(width, height), C2Fence())); | 
|  | } | 
|  |  | 
|  | // GraphicMetadataBuffer | 
|  | GraphicMetadataBuffer::GraphicMetadataBuffer( | 
|  | const sp<AMessage> &format, | 
|  | const std::shared_ptr<C2Allocator> &alloc) | 
|  | : Codec2Buffer(format, new ABuffer(sizeof(VideoNativeMetadata))), | 
|  | mAlloc(alloc) { | 
|  | ((VideoNativeMetadata *)base())->pBuffer = nullptr; | 
|  | } | 
|  |  | 
|  | std::shared_ptr<C2Buffer> GraphicMetadataBuffer::asC2Buffer() { | 
|  | #ifdef __LP64__ | 
|  | static std::once_flag s_checkOnce; | 
|  | static bool s_is64bitOk {true}; | 
|  | std::call_once(s_checkOnce, [&](){ | 
|  | const std::string abi32list = | 
|  | ::android::base::GetProperty("ro.product.cpu.abilist32", ""); | 
|  | if (!abi32list.empty()) { | 
|  | int32_t inputSurfaceSetting = | 
|  | ::android::base::GetIntProperty("debug.stagefright.c2inputsurface", int32_t(0)); | 
|  | s_is64bitOk = inputSurfaceSetting != 0; | 
|  | } | 
|  | }); | 
|  |  | 
|  | if (!s_is64bitOk) { | 
|  | ALOGE("GraphicMetadataBuffer does not work in 32+64 system if compiled as 64-bit object"\ | 
|  | "when debug.stagefright.c2inputsurface is set to 0"); | 
|  | return nullptr; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | VideoNativeMetadata *meta = (VideoNativeMetadata *)base(); | 
|  | ANativeWindowBuffer *buffer = (ANativeWindowBuffer *)meta->pBuffer; | 
|  | if (buffer == nullptr) { | 
|  | ALOGD("VideoNativeMetadata contains null buffer"); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | ALOGV("VideoNativeMetadata: %dx%d", buffer->width, buffer->height); | 
|  | C2Handle *handle = WrapNativeCodec2GrallocHandle( | 
|  | buffer->handle, | 
|  | buffer->width, | 
|  | buffer->height, | 
|  | buffer->format, | 
|  | buffer->usage, | 
|  | buffer->stride); | 
|  | std::shared_ptr<C2GraphicAllocation> alloc; | 
|  | c2_status_t err = mAlloc->priorGraphicAllocation(handle, &alloc); | 
|  | if (err != C2_OK) { | 
|  | ALOGD("Failed to wrap VideoNativeMetadata into C2GraphicAllocation"); | 
|  | native_handle_close(handle); | 
|  | native_handle_delete(handle); | 
|  | return nullptr; | 
|  | } | 
|  | std::shared_ptr<C2GraphicBlock> block = _C2BlockFactory::CreateGraphicBlock(alloc); | 
|  |  | 
|  | meta->pBuffer = 0; | 
|  | // TODO: wrap this in C2Fence so that the component can wait when it | 
|  | //       actually starts processing. | 
|  | if (meta->nFenceFd >= 0) { | 
|  | sp<Fence> fence(new Fence(meta->nFenceFd)); | 
|  | fence->waitForever(LOG_TAG); | 
|  | } | 
|  | return C2Buffer::CreateGraphicBuffer( | 
|  | block->share(C2Rect(buffer->width, buffer->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; | 
|  | } | 
|  | ATRACE_BEGIN("ConstGraphicBlockBuffer::Allocate block->map()"); | 
|  | std::unique_ptr<const C2GraphicView> view(std::make_unique<const C2GraphicView>( | 
|  | buffer->data().graphicBlocks()[0].map().get())); | 
|  | ATRACE_END(); | 
|  | std::unique_ptr<const C2GraphicView> holder; | 
|  |  | 
|  | GraphicView2MediaImageConverter converter(*view, format, false /* copy */); | 
|  | 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.copyToMediaImage(); | 
|  | // 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; | 
|  | } | 
|  | int32_t colorFormat = COLOR_FormatYUV420Flexible; | 
|  | int32_t bpp = 12;  // 8(Y) + 2(U) + 2(V) | 
|  | if (format->findInt32(KEY_COLOR_FORMAT, &colorFormat)) { | 
|  | if (colorFormat == COLOR_FormatYUVP010) { | 
|  | bpp = 24;  // 16(Y) + 4(U) + 4(V) | 
|  | } | 
|  | } | 
|  | sp<ABuffer> aBuffer(alloc(align(width, 16) * align(height, 16) * bpp / 8)); | 
|  | if (aBuffer == nullptr) { | 
|  | ALOGD("%s: failed to allocate buffer", __func__); | 
|  | return nullptr; | 
|  | } | 
|  | 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) { | 
|  | setImageData(imageData); | 
|  | } | 
|  |  | 
|  | std::shared_ptr<C2Buffer> ConstGraphicBlockBuffer::asC2Buffer() { | 
|  | return mBufferRef; | 
|  | } | 
|  |  | 
|  | void ConstGraphicBlockBuffer::clearC2BufferRefs() { | 
|  | mView.reset(); | 
|  | mBufferRef.reset(); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | ATRACE_BEGIN("ConstGraphicBlockBuffer::canCopy block->map()"); | 
|  | GraphicView2MediaImageConverter converter( | 
|  | buffer->data().graphicBlocks()[0].map().get(), | 
|  | // FIXME: format() is not const, but we cannot change it, so do a const cast here | 
|  | const_cast<ConstGraphicBlockBuffer *>(this)->format(), | 
|  | true /* copy */); | 
|  | ATRACE_END(); | 
|  | 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(), format(), true /* copy */); | 
|  | 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; | 
|  | } | 
|  | setRange(0, aBuffer->size());  // align size info | 
|  | converter.copyToMediaImage(); | 
|  | setImageData(converter.imageData()); | 
|  | mBufferRef = buffer; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // EncryptedLinearBlockBuffer | 
|  |  | 
|  | EncryptedLinearBlockBuffer::EncryptedLinearBlockBuffer( | 
|  | const sp<AMessage> &format, | 
|  | const std::shared_ptr<C2LinearBlock> &block, | 
|  | const sp<IMemory> &memory, | 
|  | int32_t heapSeqNum) | 
|  | // TODO: Using unsecurePointer() has some associated security pitfalls | 
|  | //       (see declaration for details). | 
|  | //       Either document why it is safe in this case or address the | 
|  | //       issue (e.g. by copying). | 
|  | : Codec2Buffer(format, new ABuffer(memory->unsecurePointer(), memory->size())), | 
|  | mBlock(block), | 
|  | mMemory(memory), | 
|  | mHeapSeqNum(heapSeqNum) { | 
|  | } | 
|  |  | 
|  | std::shared_ptr<C2Buffer> EncryptedLinearBlockBuffer::asC2Buffer() { | 
|  | return C2Buffer::CreateLinearBuffer(mBlock->share(offset(), size(), C2Fence())); | 
|  | } | 
|  |  | 
|  | void EncryptedLinearBlockBuffer::fillSourceBuffer( | 
|  | hardware::drm::V1_0::SharedBuffer *source) { | 
|  | BufferChannelBase::IMemoryToSharedBuffer(mMemory, mHeapSeqNum, source); | 
|  | } | 
|  |  | 
|  | void EncryptedLinearBlockBuffer::fillSourceBuffer( | 
|  | hardware::cas::native::V1_0::SharedBuffer *source) { | 
|  | ssize_t offset; | 
|  | size_t size; | 
|  |  | 
|  | mHidlMemory = hardware::fromHeap(mMemory->getMemory(&offset, &size)); | 
|  | source->heapBase = *mHidlMemory; | 
|  | source->offset = offset; | 
|  | source->size = size; | 
|  | } | 
|  |  | 
|  | bool EncryptedLinearBlockBuffer::copyDecryptedContent( | 
|  | const sp<IMemory> &decrypted, size_t length) { | 
|  | C2WriteView view = mBlock->map().get(); | 
|  | if (view.error() != C2_OK) { | 
|  | return false; | 
|  | } | 
|  | if (view.size() < length) { | 
|  | return false; | 
|  | } | 
|  | memcpy(view.data(), decrypted->unsecurePointer(), length); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool EncryptedLinearBlockBuffer::copyDecryptedContentFromMemory(size_t length) { | 
|  | return copyDecryptedContent(mMemory, length); | 
|  | } | 
|  |  | 
|  | native_handle_t *EncryptedLinearBlockBuffer::handle() const { | 
|  | return const_cast<native_handle_t *>(mBlock->handle()); | 
|  | } | 
|  |  | 
|  | using ::aidl::android::hardware::graphics::common::Cta861_3; | 
|  | using ::aidl::android::hardware::graphics::common::Smpte2086; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class GrallocBuffer { | 
|  | public: | 
|  | GrallocBuffer(const C2Handle *const handle) : mBuffer(nullptr) { | 
|  | GraphicBufferMapper& mapper = GraphicBufferMapper::get(); | 
|  |  | 
|  | // Unwrap raw buffer handle from the C2Handle | 
|  | native_handle_t *nh = UnwrapNativeCodec2GrallocHandle(handle); | 
|  | if (!nh) { | 
|  | return; | 
|  | } | 
|  | // Import the raw handle so IMapper can use the buffer. The imported | 
|  | // handle must be freed when the client is done with the buffer. | 
|  | status_t status = mapper.importBufferNoValidate( | 
|  | nh, | 
|  | &mBuffer); | 
|  |  | 
|  | if (status != OK) { | 
|  | ALOGE("Failed to import buffer. Status: %d.", status); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // TRICKY: UnwrapNativeCodec2GrallocHandle creates a new handle but | 
|  | //         does not clone the fds. Thus we need to delete the handle | 
|  | //         without closing it. | 
|  | native_handle_delete(nh); | 
|  | } | 
|  |  | 
|  | ~GrallocBuffer() { | 
|  | GraphicBufferMapper& mapper = GraphicBufferMapper::get(); | 
|  | if (mBuffer) { | 
|  | // Free the imported buffer handle. This does not release the | 
|  | // underlying buffer itself. | 
|  | mapper.freeBuffer(mBuffer); | 
|  | } | 
|  | } | 
|  |  | 
|  | buffer_handle_t get() const { return mBuffer; } | 
|  | operator bool() const { return (mBuffer != nullptr); } | 
|  | private: | 
|  | buffer_handle_t mBuffer; | 
|  | }; | 
|  |  | 
|  | }  // namspace | 
|  |  | 
|  | c2_status_t GetHdrMetadataFromGralloc4Handle( | 
|  | const C2Handle *const handle, | 
|  | std::shared_ptr<C2StreamHdrStaticMetadataInfo::input> *staticInfo, | 
|  | std::shared_ptr<C2StreamHdrDynamicMetadataInfo::input> *dynamicInfo) { | 
|  | c2_status_t err = C2_OK; | 
|  | GraphicBufferMapper& mapper = GraphicBufferMapper::get(); | 
|  | GrallocBuffer buffer(handle); | 
|  | if (!buffer) { | 
|  | // Gralloc4 not supported; nothing to do | 
|  | return err; | 
|  | } | 
|  | if (staticInfo) { | 
|  | ALOGV("Grabbing static HDR info from gralloc metadata"); | 
|  | staticInfo->reset(new C2StreamHdrStaticMetadataInfo::input(0u)); | 
|  | memset(&(*staticInfo)->mastering, 0, sizeof((*staticInfo)->mastering)); | 
|  | (*staticInfo)->maxCll = 0; | 
|  | (*staticInfo)->maxFall = 0; | 
|  |  | 
|  | std::optional<Smpte2086> smpte2086; | 
|  | status_t status = mapper.getSmpte2086(buffer.get(), &smpte2086); | 
|  | if (status != OK) { | 
|  | err = C2_CORRUPTED; | 
|  | } else { | 
|  | if (smpte2086) { | 
|  | (*staticInfo)->mastering.red.x    = smpte2086->primaryRed.x; | 
|  | (*staticInfo)->mastering.red.y    = smpte2086->primaryRed.y; | 
|  | (*staticInfo)->mastering.green.x  = smpte2086->primaryGreen.x; | 
|  | (*staticInfo)->mastering.green.y  = smpte2086->primaryGreen.y; | 
|  | (*staticInfo)->mastering.blue.x   = smpte2086->primaryBlue.x; | 
|  | (*staticInfo)->mastering.blue.y   = smpte2086->primaryBlue.y; | 
|  | (*staticInfo)->mastering.white.x  = smpte2086->whitePoint.x; | 
|  | (*staticInfo)->mastering.white.y  = smpte2086->whitePoint.y; | 
|  |  | 
|  | (*staticInfo)->mastering.maxLuminance = smpte2086->maxLuminance; | 
|  | (*staticInfo)->mastering.minLuminance = smpte2086->minLuminance; | 
|  | } | 
|  | } | 
|  |  | 
|  | std::optional<Cta861_3> cta861_3; | 
|  | status = mapper.getCta861_3(buffer.get(), &cta861_3); | 
|  | if (status != OK) { | 
|  | err = C2_CORRUPTED; | 
|  | } else { | 
|  | if (cta861_3) { | 
|  | (*staticInfo)->maxCll   = cta861_3->maxContentLightLevel; | 
|  | (*staticInfo)->maxFall  = cta861_3->maxFrameAverageLightLevel; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (err != C2_OK) { | 
|  | staticInfo->reset(); | 
|  | } | 
|  |  | 
|  | if (dynamicInfo) { | 
|  | ALOGV("Grabbing dynamic HDR info from gralloc metadata"); | 
|  | dynamicInfo->reset(); | 
|  | std::optional<std::vector<uint8_t>> vec; | 
|  | status_t status = mapper.getSmpte2094_40(buffer.get(), &vec); | 
|  | if (status != OK) { | 
|  | dynamicInfo->reset(); | 
|  | err = C2_CORRUPTED; | 
|  | } else { | 
|  | if (vec) { | 
|  | *dynamicInfo = C2StreamHdrDynamicMetadataInfo::input::AllocShared( | 
|  | vec->size(), 0u, C2Config::HDR_DYNAMIC_METADATA_TYPE_SMPTE_2094_40); | 
|  | memcpy((*dynamicInfo)->m.data, vec->data(), vec->size()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | c2_status_t SetMetadataToGralloc4Handle( | 
|  | android_dataspace_t dataSpace, | 
|  | const std::shared_ptr<const C2StreamHdrStaticMetadataInfo::output> &staticInfo, | 
|  | const std::shared_ptr<const C2StreamHdrDynamicMetadataInfo::output> &dynamicInfo, | 
|  | const C2Handle *const handle) { | 
|  | c2_status_t err = C2_OK; | 
|  | GraphicBufferMapper& mapper = GraphicBufferMapper::get(); | 
|  | GrallocBuffer buffer(handle); | 
|  | if (!buffer) { | 
|  | // Gralloc4 not supported; nothing to do | 
|  | return err; | 
|  | } | 
|  | status_t status = mapper.setDataspace(buffer.get(), static_cast<ui::Dataspace>(dataSpace)); | 
|  | if (status != OK) { | 
|  | err = C2_CORRUPTED; | 
|  | } | 
|  | if (staticInfo && *staticInfo) { | 
|  | ALOGV("Setting static HDR info as gralloc metadata"); | 
|  | std::optional<Smpte2086> smpte2086 = Smpte2086{ | 
|  | {staticInfo->mastering.red.x, staticInfo->mastering.red.y}, | 
|  | {staticInfo->mastering.green.x, staticInfo->mastering.green.y}, | 
|  | {staticInfo->mastering.blue.x, staticInfo->mastering.blue.y}, | 
|  | {staticInfo->mastering.white.x, staticInfo->mastering.white.y}, | 
|  | staticInfo->mastering.maxLuminance, | 
|  | staticInfo->mastering.minLuminance, | 
|  | }; | 
|  | if (0.0 <= smpte2086->primaryRed.x && smpte2086->primaryRed.x <= 1.0 | 
|  | && 0.0 <= smpte2086->primaryRed.y && smpte2086->primaryRed.y <= 1.0 | 
|  | && 0.0 <= smpte2086->primaryGreen.x && smpte2086->primaryGreen.x <= 1.0 | 
|  | && 0.0 <= smpte2086->primaryGreen.y && smpte2086->primaryGreen.y <= 1.0 | 
|  | && 0.0 <= smpte2086->primaryBlue.x && smpte2086->primaryBlue.x <= 1.0 | 
|  | && 0.0 <= smpte2086->primaryBlue.y && smpte2086->primaryBlue.y <= 1.0 | 
|  | && 0.0 <= smpte2086->whitePoint.x && smpte2086->whitePoint.x <= 1.0 | 
|  | && 0.0 <= smpte2086->whitePoint.y && smpte2086->whitePoint.y <= 1.0 | 
|  | && 0.0 <= smpte2086->maxLuminance && 0.0 <= smpte2086->minLuminance) { | 
|  | status = mapper.setSmpte2086(buffer.get(), smpte2086); | 
|  | if (status != OK) { | 
|  | err = C2_CORRUPTED; | 
|  | } | 
|  | } | 
|  | std::optional<Cta861_3> cta861_3 = Cta861_3{ | 
|  | staticInfo->maxCll, | 
|  | staticInfo->maxFall, | 
|  | }; | 
|  | if (0.0 <= cta861_3->maxContentLightLevel && 0.0 <= cta861_3->maxFrameAverageLightLevel) { | 
|  | status = mapper.setCta861_3(buffer.get(), cta861_3); | 
|  | if (status != OK) { | 
|  | err = C2_CORRUPTED; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (dynamicInfo && *dynamicInfo && dynamicInfo->flexCount() > 0) { | 
|  | ALOGV("Setting dynamic HDR info as gralloc metadata"); | 
|  | if (dynamicInfo->m.type_ == C2Config::HDR_DYNAMIC_METADATA_TYPE_SMPTE_2094_40) { | 
|  | std::optional<std::vector<uint8_t>> smpte2094_40 = std::vector<uint8_t>(); | 
|  | smpte2094_40->resize(dynamicInfo->flexCount()); | 
|  | memcpy(smpte2094_40->data(), dynamicInfo->m.data, dynamicInfo->flexCount()); | 
|  |  | 
|  | status = mapper.setSmpte2094_40(buffer.get(), smpte2094_40); | 
|  | if (status != OK) { | 
|  | err = C2_CORRUPTED; | 
|  | } | 
|  | } else { | 
|  | err = C2_BAD_VALUE; | 
|  | } | 
|  | } | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | }  // namespace android |