Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29e3c4d6 authored by Wonsik Kim's avatar Wonsik Kim
Browse files

codec2: add P010 support

Bug: 163020028
Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small
Test: atest ccodec_unit_test:RawGraphicOutputBuffersTest
Change-Id: Ibd0da668c510665a8838f0d7e6abd264814d6526
parent 486aa5da
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
@@ -353,6 +353,66 @@ public:
                        mediaImage->mPlane[mediaImage->V].mVertSubsampling = 2;
                        break;

                    case COLOR_FormatYUVP010:
                        if (!copy) {
                            // 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];
                                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 += std::abs(plane.rowInc) * align(mHeight, 64)
                                        / plane.rowSampling / plane.colSampling
                                        * divUp(mAllocatedDepth, 8u);
                            }

                            if ((maxPtr - minPtr + 1) <= 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 + divUp(mAllocatedDepth, 8u));
                                break;
                            }
                        }
                        mediaImage->mPlane[mediaImage->Y].mOffset = 0;
                        mediaImage->mPlane[mediaImage->Y].mColInc = 2;
                        mediaImage->mPlane[mediaImage->Y].mRowInc = stride * 2;
                        mediaImage->mPlane[mediaImage->Y].mHorizSubsampling = 1;
                        mediaImage->mPlane[mediaImage->Y].mVertSubsampling = 1;

                        mediaImage->mPlane[mediaImage->U].mOffset = stride * vStride * 2;
                        mediaImage->mPlane[mediaImage->U].mColInc = 4;
                        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 * 2 + 2;
                        mediaImage->mPlane[mediaImage->V].mColInc = 4;
                        mediaImage->mPlane[mediaImage->V].mRowInc = stride * 2;
                        mediaImage->mPlane[mediaImage->V].mHorizSubsampling = 2;
                        mediaImage->mPlane[mediaImage->V].mVertSubsampling = 2;
                        break;

                    default:
                        ALOGD("Converter: incompactible color format (%d) for YUV layout", mColorFormat);
                        mInitCheck = BAD_VALUE;
+314 −2
Original line number Diff line number Diff line
@@ -18,11 +18,12 @@

#include <gtest/gtest.h>

#include <media/stagefright/foundation/AString.h>
#include <codec2/hidl/client.h>
#include <media/stagefright/MediaCodecConstants.h>

#include <C2BlockInternal.h>
#include <C2PlatformSupport.h>
#include <Codec2Mapper.h>

namespace android {

@@ -105,6 +106,318 @@ TEST(RawGraphicOutputBuffersTest, ChangeNumSlots) {
    }
}

TEST(RawGraphicOutputBuffersTest, FlexYuvColorFormat) {
    constexpr int32_t kWidth = 320;
    constexpr int32_t kHeight = 240;

    std::vector<uint32_t> flexPixelFormats({HAL_PIXEL_FORMAT_YCbCr_420_888});
    std::shared_ptr<Codec2Client> client = Codec2Client::CreateFromService("default");
    if (client) {
        // Query vendor format for Flexible YUV
        std::vector<std::unique_ptr<C2Param>> heapParams;
        C2StoreFlexiblePixelFormatDescriptorsInfo *pixelFormatInfo = nullptr;
        if (client->query(
                    {},
                    {C2StoreFlexiblePixelFormatDescriptorsInfo::PARAM_TYPE},
                    C2_MAY_BLOCK,
                    &heapParams) == C2_OK
                && heapParams.size() == 1u) {
            pixelFormatInfo = C2StoreFlexiblePixelFormatDescriptorsInfo::From(
                    heapParams[0].get());
        } else {
            pixelFormatInfo = nullptr;
        }
        if (pixelFormatInfo && *pixelFormatInfo) {
            for (size_t i = 0; i < pixelFormatInfo->flexCount(); ++i) {
                const C2FlexiblePixelFormatDescriptorStruct &desc =
                    pixelFormatInfo->m.values[i];
                if (desc.bitDepth != 8
                        || desc.subsampling != C2Color::YUV_420
                        // TODO(b/180076105): some devices report wrong layouts
                        // || desc.layout == C2Color::INTERLEAVED_PACKED
                        // || desc.layout == C2Color::INTERLEAVED_ALIGNED
                        || desc.layout == C2Color::UNKNOWN_LAYOUT) {
                    continue;
                }
                flexPixelFormats.push_back(desc.pixelFormat);
            }
        }
    }

    for (uint32_t pixelFormat : flexPixelFormats) {
        std::shared_ptr<RawGraphicOutputBuffers> buffers =
            std::make_shared<RawGraphicOutputBuffers>(
                    AStringPrintf("test pixel format 0x%x", pixelFormat).c_str());

        sp<AMessage> format{new AMessage};
        format->setInt32(KEY_WIDTH, kWidth);
        format->setInt32(KEY_HEIGHT, kHeight);
        format->setInt32(KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
        int32_t fwkPixelFormat = 0;
        if (C2Mapper::mapPixelFormatCodecToFramework(pixelFormat, &fwkPixelFormat)) {
            format->setInt32("android._color-format", fwkPixelFormat);
        }
        buffers->setFormat(format);

        std::shared_ptr<C2BlockPool> pool;
        ASSERT_EQ(OK, GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, nullptr, &pool));

        std::shared_ptr<C2GraphicBlock> block;
        ASSERT_EQ(OK, pool->fetchGraphicBlock(
                kWidth, kHeight, pixelFormat,
                C2MemoryUsage{C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block));

        {
            C2GraphicView view = block->map().get();
            C2PlanarLayout layout = view.layout();

            // Verify the block is in YUV420 format
            ASSERT_EQ(C2PlanarLayout::TYPE_YUV, layout.type);
            ASSERT_EQ(3u, layout.numPlanes);
            const C2PlaneInfo& yPlane = layout.planes[C2PlanarLayout::PLANE_Y];
            const C2PlaneInfo& uPlane = layout.planes[C2PlanarLayout::PLANE_U];
            const C2PlaneInfo& vPlane = layout.planes[C2PlanarLayout::PLANE_V];

            // Y plane
            ASSERT_EQ(1u, yPlane.colSampling);
            ASSERT_EQ(1u, yPlane.rowSampling);
            ASSERT_EQ(8u, yPlane.allocatedDepth);
            ASSERT_EQ(8u, yPlane.bitDepth);
            ASSERT_EQ(0u, yPlane.rightShift);

            // U plane
            ASSERT_EQ(2u, uPlane.colSampling);
            ASSERT_EQ(2u, uPlane.rowSampling);
            ASSERT_EQ(8u, uPlane.allocatedDepth);
            ASSERT_EQ(8u, uPlane.bitDepth);
            ASSERT_EQ(0u, uPlane.rightShift);

            // V plane
            ASSERT_EQ(2u, vPlane.colSampling);
            ASSERT_EQ(2u, vPlane.rowSampling);
            ASSERT_EQ(8u, vPlane.allocatedDepth);
            ASSERT_EQ(8u, vPlane.bitDepth);
            ASSERT_EQ(0u, vPlane.rightShift);

            uint8_t *yRowPtr = view.data()[C2PlanarLayout::PLANE_Y];
            uint8_t *uRowPtr = view.data()[C2PlanarLayout::PLANE_U];
            uint8_t *vRowPtr = view.data()[C2PlanarLayout::PLANE_V];
            for (int32_t row = 0; row < kHeight; ++row) {
                uint8_t *yPtr = yRowPtr;
                uint8_t *uPtr = uRowPtr;
                uint8_t *vPtr = vRowPtr;
                for (int32_t col = 0; col < kWidth; ++col) {
                    *yPtr = ((row + col) & 0xFF);
                    yPtr += yPlane.colInc;

                    if (row < kHeight / 2 && col < kWidth / 2) {
                        *uPtr = ((row + col + 1) & 0xFF);
                        *vPtr = ((row + col + 2) & 0xFF);
                        uPtr += uPlane.colInc;
                        vPtr += vPlane.colInc;
                    }
                }
                yRowPtr += yPlane.rowInc;
                if (row < kHeight / 2) {
                    uRowPtr += uPlane.rowInc;
                    vRowPtr += vPlane.rowInc;
                }
            }
        }

        std::shared_ptr<C2Buffer> c2Buffer = C2Buffer::CreateGraphicBuffer(block->share(
                block->crop(), C2Fence{}));
        size_t index;
        sp<MediaCodecBuffer> clientBuffer;
        ASSERT_EQ(OK, buffers->registerBuffer(c2Buffer, &index, &clientBuffer));
        ASSERT_NE(nullptr, clientBuffer);
        sp<ABuffer> imageData;
        ASSERT_TRUE(clientBuffer->format()->findBuffer("image-data", &imageData));
        MediaImage2 *img = (MediaImage2 *)imageData->data();
        ASSERT_EQ(MediaImage2::MEDIA_IMAGE_TYPE_YUV, img->mType);
        ASSERT_EQ(3u, img->mNumPlanes);
        ASSERT_EQ(kWidth, img->mWidth);
        ASSERT_EQ(kHeight, img->mHeight);
        ASSERT_EQ(8u, img->mBitDepth);
        ASSERT_EQ(8u, img->mBitDepthAllocated);
        const MediaImage2::PlaneInfo &yPlane = img->mPlane[MediaImage2::Y];
        const MediaImage2::PlaneInfo &uPlane = img->mPlane[MediaImage2::U];
        const MediaImage2::PlaneInfo &vPlane = img->mPlane[MediaImage2::V];
        ASSERT_EQ(1u, yPlane.mHorizSubsampling);
        ASSERT_EQ(1u, yPlane.mVertSubsampling);
        ASSERT_EQ(2u, uPlane.mHorizSubsampling);
        ASSERT_EQ(2u, uPlane.mVertSubsampling);
        ASSERT_EQ(2u, vPlane.mHorizSubsampling);
        ASSERT_EQ(2u, vPlane.mVertSubsampling);

        uint8_t *yRowPtr = clientBuffer->data() + yPlane.mOffset;
        uint8_t *uRowPtr = clientBuffer->data() + uPlane.mOffset;
        uint8_t *vRowPtr = clientBuffer->data() + vPlane.mOffset;
        for (int32_t row = 0; row < kHeight; ++row) {
            uint8_t *yPtr = yRowPtr;
            uint8_t *uPtr = uRowPtr;
            uint8_t *vPtr = vRowPtr;
            for (int32_t col = 0; col < kWidth; ++col) {
                ASSERT_EQ((row + col) & 0xFF, *yPtr);
                yPtr += yPlane.mColInc;
                if (row < kHeight / 2 && col < kWidth / 2) {
                    ASSERT_EQ((row + col + 1) & 0xFF, *uPtr);
                    ASSERT_EQ((row + col + 2) & 0xFF, *vPtr);
                    uPtr += uPlane.mColInc;
                    vPtr += vPlane.mColInc;
                }
            }
            yRowPtr += yPlane.mRowInc;
            if (row < kHeight / 2) {
                uRowPtr += uPlane.mRowInc;
                vRowPtr += vPlane.mRowInc;
            }
        }
    }
}

TEST(RawGraphicOutputBuffersTest, P010ColorFormat) {
    constexpr int32_t kWidth = 320;
    constexpr int32_t kHeight = 240;

    std::shared_ptr<RawGraphicOutputBuffers> buffers =
        std::make_shared<RawGraphicOutputBuffers>("test P010");

    sp<AMessage> format{new AMessage};
    format->setInt32(KEY_WIDTH, kWidth);
    format->setInt32(KEY_HEIGHT, kHeight);
    format->setInt32(KEY_COLOR_FORMAT, COLOR_FormatYUVP010);
    int32_t fwkPixelFormat = 0;
    if (C2Mapper::mapPixelFormatCodecToFramework(HAL_PIXEL_FORMAT_YCBCR_P010, &fwkPixelFormat)) {
        format->setInt32("android._color-format", fwkPixelFormat);
    }
    buffers->setFormat(format);

    std::shared_ptr<C2BlockPool> pool;
    ASSERT_EQ(OK, GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, nullptr, &pool));

    std::shared_ptr<C2GraphicBlock> block;
    c2_status_t err = pool->fetchGraphicBlock(
            kWidth, kHeight, HAL_PIXEL_FORMAT_YCBCR_P010,
            C2MemoryUsage{C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
    if (err != C2_OK) {
        GTEST_SKIP();
    }

    {
        C2GraphicView view = block->map().get();
        C2PlanarLayout layout = view.layout();

        // Verify the block is in YUV420 format
        ASSERT_EQ(C2PlanarLayout::TYPE_YUV, layout.type);
        ASSERT_EQ(3u, layout.numPlanes);
        const C2PlaneInfo& yPlane = layout.planes[C2PlanarLayout::PLANE_Y];
        const C2PlaneInfo& uPlane = layout.planes[C2PlanarLayout::PLANE_U];
        const C2PlaneInfo& vPlane = layout.planes[C2PlanarLayout::PLANE_V];

        // Y plane
        ASSERT_EQ(1u, yPlane.colSampling);
        ASSERT_EQ(1u, yPlane.rowSampling);
        ASSERT_EQ(16u, yPlane.allocatedDepth);
        ASSERT_EQ(10u, yPlane.bitDepth);
        ASSERT_EQ(6u, yPlane.rightShift);

        // U plane
        ASSERT_EQ(2u, uPlane.colSampling);
        ASSERT_EQ(2u, uPlane.rowSampling);
        ASSERT_EQ(16u, uPlane.allocatedDepth);
        ASSERT_EQ(10u, uPlane.bitDepth);
        ASSERT_EQ(6u, uPlane.rightShift);

        // V plane
        ASSERT_EQ(2u, vPlane.colSampling);
        ASSERT_EQ(2u, vPlane.rowSampling);
        ASSERT_EQ(16u, vPlane.allocatedDepth);
        ASSERT_EQ(10u, vPlane.bitDepth);
        ASSERT_EQ(6u, vPlane.rightShift);

        uint8_t *yRowPtr = view.data()[C2PlanarLayout::PLANE_Y];
        uint8_t *uRowPtr = view.data()[C2PlanarLayout::PLANE_U];
        uint8_t *vRowPtr = view.data()[C2PlanarLayout::PLANE_V];
        for (int32_t row = 0; row < kHeight; ++row) {
            uint8_t *yPtr = yRowPtr;
            uint8_t *uPtr = uRowPtr;
            uint8_t *vPtr = vRowPtr;
            for (int32_t col = 0; col < kWidth; ++col) {
                yPtr[0] = ((row + col) & 0x3) << 6;
                yPtr[1] = ((row + col) & 0x3FC) >> 2;
                yPtr += yPlane.colInc;

                if (row < kHeight / 2 && col < kWidth / 2) {
                    uPtr[0] = ((row + col + 1) & 0x3) << 6;
                    uPtr[1] = ((row + col + 1) & 0x3FC) >> 2;
                    vPtr[0] = ((row + col + 2) & 0x3) << 6;
                    vPtr[1] = ((row + col + 2) & 0x3FC) >> 2;
                    uPtr += uPlane.colInc;
                    vPtr += vPlane.colInc;
                }
            }
            yRowPtr += yPlane.rowInc;
            if (row < kHeight / 2) {
                uRowPtr += uPlane.rowInc;
                vRowPtr += vPlane.rowInc;
            }
        }
    }

    std::shared_ptr<C2Buffer> c2Buffer = C2Buffer::CreateGraphicBuffer(block->share(
            block->crop(), C2Fence{}));
    size_t index;
    sp<MediaCodecBuffer> clientBuffer;
    ASSERT_EQ(OK, buffers->registerBuffer(c2Buffer, &index, &clientBuffer));
    ASSERT_NE(nullptr, clientBuffer);
    sp<ABuffer> imageData;
    ASSERT_TRUE(clientBuffer->format()->findBuffer("image-data", &imageData));
    MediaImage2 *img = (MediaImage2 *)imageData->data();
    ASSERT_EQ(MediaImage2::MEDIA_IMAGE_TYPE_YUV, img->mType);
    ASSERT_EQ(3u, img->mNumPlanes);
    ASSERT_EQ(kWidth, img->mWidth);
    ASSERT_EQ(kHeight, img->mHeight);
    ASSERT_EQ(10u, img->mBitDepth);
    ASSERT_EQ(16u, img->mBitDepthAllocated);
    const MediaImage2::PlaneInfo &yPlane = img->mPlane[MediaImage2::Y];
    const MediaImage2::PlaneInfo &uPlane = img->mPlane[MediaImage2::U];
    const MediaImage2::PlaneInfo &vPlane = img->mPlane[MediaImage2::V];
    ASSERT_EQ(1u, yPlane.mHorizSubsampling);
    ASSERT_EQ(1u, yPlane.mVertSubsampling);
    ASSERT_EQ(2u, uPlane.mHorizSubsampling);
    ASSERT_EQ(2u, uPlane.mVertSubsampling);
    ASSERT_EQ(2u, vPlane.mHorizSubsampling);
    ASSERT_EQ(2u, vPlane.mVertSubsampling);

    uint8_t *yRowPtr = clientBuffer->data() + yPlane.mOffset;
    uint8_t *uRowPtr = clientBuffer->data() + uPlane.mOffset;
    uint8_t *vRowPtr = clientBuffer->data() + vPlane.mOffset;
    for (int32_t row = 0; row < kHeight; ++row) {
        uint8_t *yPtr = yRowPtr;
        uint8_t *uPtr = uRowPtr;
        uint8_t *vPtr = vRowPtr;
        for (int32_t col = 0; col < kWidth; ++col) {
            ASSERT_EQ(((row + col) & 0x3) << 6, yPtr[0]);
            ASSERT_EQ(((row + col) & 0x3FC) >> 2, yPtr[1]);
            yPtr += yPlane.mColInc;
            if (row < kHeight / 2 && col < kWidth / 2) {
                ASSERT_EQ(((row + col + 1) & 0x3) << 6, uPtr[0]);
                ASSERT_EQ(((row + col + 1) & 0x3FC) >> 2, uPtr[1]);
                ASSERT_EQ(((row + col + 2) & 0x3) << 6, vPtr[0]);
                ASSERT_EQ(((row + col + 2) & 0x3FC) >> 2, vPtr[1]);
                uPtr += uPlane.mColInc;
                vPtr += vPlane.mColInc;
            }
        }
        yRowPtr += yPlane.mRowInc;
        if (row < kHeight / 2) {
            uRowPtr += uPlane.mRowInc;
            vRowPtr += vPlane.mRowInc;
        }
    }
}

class TestGraphicAllocation : public C2GraphicAllocation {
public:
    TestGraphicAllocation(
@@ -407,7 +720,6 @@ public:
                }
            }
        }

        size_t yPlaneSize = stride * kHeight;
        size_t uvPlaneSize = stride * kHeight / 4;
        size_t capacity = yPlaneSize + uvPlaneSize * 2;
+6 −3
Original line number Diff line number Diff line
@@ -967,8 +967,9 @@ bool C2Mapper::mapPixelFormatFrameworkToCodec(
            *c2Value = HAL_PIXEL_FORMAT_YV12;
            return true;
        default:
            // TODO: support some sort of passthrough
            return false;
            // Passthrough
            *c2Value = uint32_t(frameworkValue);
            return true;
    }
}

@@ -984,6 +985,8 @@ bool C2Mapper::mapPixelFormatCodecToFramework(
            *frameworkValue = COLOR_FormatYUV420Flexible;
            return true;
        default:
            return false;
            // Passthrough
            *frameworkValue = int32_t(c2Value);
            return true;
    }
}
+159 −3

File changed.

Preview size limit exceeded, changes collapsed.

+1 −0
Original line number Diff line number Diff line
@@ -619,6 +619,7 @@ constexpr int32_t COLOR_FormatYUV422Planar = 22;
constexpr int32_t COLOR_FormatYUV422SemiPlanar        = 24;
constexpr int32_t COLOR_FormatYUV444Flexible          = 0x7F444888;
constexpr int32_t COLOR_FormatYUV444Interleaved       = 29;
constexpr int32_t COLOR_FormatYUVP010                 = 54;
constexpr int32_t COLOR_QCOM_FormatYUV420SemiPlanar   = 0x7fa30c00;
constexpr int32_t COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100;