blob: 3bb659353a1080ca15bbdfa9487c849e8c3fd583 [file] [log] [blame]
/*
* 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