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

Commit bc0816e6 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "CCodec: fix ByteBuffer mode image" am: d883b06d am: d410d509

Original change: https://android-review.googlesource.com/c/platform/frameworks/av/+/1516460

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Ie9070d19792107051384200f7988af6960afdda3
parents 63d29afa d410d509
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -96,6 +96,9 @@ void CCodecBuffers::handleImageData(const sp<Codec2Buffer> &buffer) {
                int32_t vstride = int32_t(offsetDelta / stride);
                newFormat->setInt32(KEY_SLICE_HEIGHT, vstride);
                ALOGD("[%s] updating vstride = %d", mName, vstride);
                buffer->setRange(
                        img->mPlane[0].mOffset,
                        buffer->size() - img->mPlane[0].mOffset);
            }
        }
        setFormat(newFormat);
+8 −6
Original line number Diff line number Diff line
@@ -276,20 +276,22 @@ public:
                            int32_t planeSize = 0;
                            for (uint32_t i = 0; i < layout.numPlanes; ++i) {
                                const C2PlaneInfo &plane = layout.planes[i];
                                ssize_t minOffset = plane.minOffset(mWidth, mHeight);
                                ssize_t maxOffset = plane.maxOffset(mWidth, mHeight);
                                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 += std::abs(plane.rowInc) * align(mHeight, 64)
                                        / plane.rowSampling / plane.colSampling
                                        * divUp(mAllocatedDepth, 8u);
                                planeSize += planeStride * divUp(mAllocatedDepth, 8u)
                                        * align(mHeight, 64) / plane.rowSampling;
                            }

                            if ((maxPtr - minPtr + 1) <= planeSize) {
                            if (minPtr == mView.data()[0] && (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
+445 −5
Original line number Diff line number Diff line
@@ -18,22 +18,31 @@

#include <gtest/gtest.h>

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

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

namespace android {

static std::shared_ptr<RawGraphicOutputBuffers> GetRawGraphicOutputBuffers(
        int32_t width, int32_t height) {
    std::shared_ptr<RawGraphicOutputBuffers> buffers =
        std::make_shared<RawGraphicOutputBuffers>("test");
    sp<AMessage> format{new AMessage};
    format->setInt32(KEY_WIDTH, width);
    format->setInt32(KEY_HEIGHT, height);
    buffers->setFormat(format);
    return buffers;
}

TEST(RawGraphicOutputBuffersTest, ChangeNumSlots) {
    constexpr int32_t kWidth = 3840;
    constexpr int32_t kHeight = 2160;

    std::shared_ptr<RawGraphicOutputBuffers> buffers =
        std::make_shared<RawGraphicOutputBuffers>("test");
    sp<AMessage> format{new AMessage};
    format->setInt32("width", kWidth);
    format->setInt32("height", kHeight);
    buffers->setFormat(format);
        GetRawGraphicOutputBuffers(kWidth, kHeight);

    std::shared_ptr<C2BlockPool> pool;
    ASSERT_EQ(OK, GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, nullptr, &pool));
@@ -96,4 +105,435 @@ TEST(RawGraphicOutputBuffersTest, ChangeNumSlots) {
    }
}

class TestGraphicAllocation : public C2GraphicAllocation {
public:
    TestGraphicAllocation(
            uint32_t width,
            uint32_t height,
            const C2PlanarLayout &layout,
            size_t capacity,
            std::vector<size_t> offsets)
        : C2GraphicAllocation(width, height),
          mLayout(layout),
          mMemory(capacity, 0xAA),
          mOffsets(offsets) {
    }

    c2_status_t map(
            C2Rect rect, C2MemoryUsage usage, C2Fence *fence,
            C2PlanarLayout *layout, uint8_t **addr) override {
        (void)rect;
        (void)usage;
        (void)fence;
        *layout = mLayout;
        for (size_t i = 0; i < mLayout.numPlanes; ++i) {
            addr[i] = mMemory.data() + mOffsets[i];
        }
        return C2_OK;
    }

    c2_status_t unmap(uint8_t **, C2Rect, C2Fence *) override { return C2_OK; }

    C2Allocator::id_t getAllocatorId() const override { return -1; }

    const C2Handle *handle() const override { return nullptr; }

    bool equals(const std::shared_ptr<const C2GraphicAllocation> &other) const override {
        return other.get() == this;
    }

private:
    C2PlanarLayout mLayout;
    std::vector<uint8_t> mMemory;
    std::vector<uint8_t *> mAddr;
    std::vector<size_t> mOffsets;
};

class LayoutTest : public ::testing::TestWithParam<std::tuple<bool, std::string, bool, int32_t>> {
private:
    static C2PlanarLayout YUVPlanarLayout(int32_t stride) {
        C2PlanarLayout layout = {
            C2PlanarLayout::TYPE_YUV,
            3,  /* numPlanes */
            3,  /* rootPlanes */
            {},  /* planes --- to be filled below */
        };
        layout.planes[C2PlanarLayout::PLANE_Y] = {
            C2PlaneInfo::CHANNEL_Y,
            1,  /* colInc */
            stride,  /* rowInc */
            1,  /* colSampling */
            1,  /* rowSampling */
            8,  /* allocatedDepth */
            8,  /* bitDepth */
            0,  /* rightShift */
            C2PlaneInfo::NATIVE,
            C2PlanarLayout::PLANE_Y,  /* rootIx */
            0,  /* offset */
        };
        layout.planes[C2PlanarLayout::PLANE_U] = {
            C2PlaneInfo::CHANNEL_CB,
            1,  /* colInc */
            stride / 2,  /* rowInc */
            2,  /* colSampling */
            2,  /* rowSampling */
            8,  /* allocatedDepth */
            8,  /* bitDepth */
            0,  /* rightShift */
            C2PlaneInfo::NATIVE,
            C2PlanarLayout::PLANE_U,  /* rootIx */
            0,  /* offset */
        };
        layout.planes[C2PlanarLayout::PLANE_V] = {
            C2PlaneInfo::CHANNEL_CR,
            1,  /* colInc */
            stride / 2,  /* rowInc */
            2,  /* colSampling */
            2,  /* rowSampling */
            8,  /* allocatedDepth */
            8,  /* bitDepth */
            0,  /* rightShift */
            C2PlaneInfo::NATIVE,
            C2PlanarLayout::PLANE_V,  /* rootIx */
            0,  /* offset */
        };
        return layout;
    }

    static C2PlanarLayout YUVSemiPlanarLayout(int32_t stride) {
        C2PlanarLayout layout = {
            C2PlanarLayout::TYPE_YUV,
            3,  /* numPlanes */
            2,  /* rootPlanes */
            {},  /* planes --- to be filled below */
        };
        layout.planes[C2PlanarLayout::PLANE_Y] = {
            C2PlaneInfo::CHANNEL_Y,
            1,  /* colInc */
            stride,  /* rowInc */
            1,  /* colSampling */
            1,  /* rowSampling */
            8,  /* allocatedDepth */
            8,  /* bitDepth */
            0,  /* rightShift */
            C2PlaneInfo::NATIVE,
            C2PlanarLayout::PLANE_Y,  /* rootIx */
            0,  /* offset */
        };
        layout.planes[C2PlanarLayout::PLANE_U] = {
            C2PlaneInfo::CHANNEL_CB,
            2,  /* colInc */
            stride,  /* rowInc */
            2,  /* colSampling */
            2,  /* rowSampling */
            8,  /* allocatedDepth */
            8,  /* bitDepth */
            0,  /* rightShift */
            C2PlaneInfo::NATIVE,
            C2PlanarLayout::PLANE_U,  /* rootIx */
            0,  /* offset */
        };
        layout.planes[C2PlanarLayout::PLANE_V] = {
            C2PlaneInfo::CHANNEL_CR,
            2,  /* colInc */
            stride,  /* rowInc */
            2,  /* colSampling */
            2,  /* rowSampling */
            8,  /* allocatedDepth */
            8,  /* bitDepth */
            0,  /* rightShift */
            C2PlaneInfo::NATIVE,
            C2PlanarLayout::PLANE_U,  /* rootIx */
            1,  /* offset */
        };
        return layout;
    }

    static C2PlanarLayout YVUSemiPlanarLayout(int32_t stride) {
        C2PlanarLayout layout = {
            C2PlanarLayout::TYPE_YUV,
            3,  /* numPlanes */
            2,  /* rootPlanes */
            {},  /* planes --- to be filled below */
        };
        layout.planes[C2PlanarLayout::PLANE_Y] = {
            C2PlaneInfo::CHANNEL_Y,
            1,  /* colInc */
            stride,  /* rowInc */
            1,  /* colSampling */
            1,  /* rowSampling */
            8,  /* allocatedDepth */
            8,  /* bitDepth */
            0,  /* rightShift */
            C2PlaneInfo::NATIVE,
            C2PlanarLayout::PLANE_Y,  /* rootIx */
            0,  /* offset */
        };
        layout.planes[C2PlanarLayout::PLANE_U] = {
            C2PlaneInfo::CHANNEL_CB,
            2,  /* colInc */
            stride,  /* rowInc */
            2,  /* colSampling */
            2,  /* rowSampling */
            8,  /* allocatedDepth */
            8,  /* bitDepth */
            0,  /* rightShift */
            C2PlaneInfo::NATIVE,
            C2PlanarLayout::PLANE_V,  /* rootIx */
            1,  /* offset */
        };
        layout.planes[C2PlanarLayout::PLANE_V] = {
            C2PlaneInfo::CHANNEL_CR,
            2,  /* colInc */
            stride,  /* rowInc */
            2,  /* colSampling */
            2,  /* rowSampling */
            8,  /* allocatedDepth */
            8,  /* bitDepth */
            0,  /* rightShift */
            C2PlaneInfo::NATIVE,
            C2PlanarLayout::PLANE_V,  /* rootIx */
            0,  /* offset */
        };
        return layout;
    }

    static std::shared_ptr<C2GraphicBlock> CreateGraphicBlock(
            uint32_t width,
            uint32_t height,
            const C2PlanarLayout &layout,
            size_t capacity,
            std::vector<size_t> offsets) {
        std::shared_ptr<C2GraphicAllocation> alloc = std::make_shared<TestGraphicAllocation>(
                width,
                height,
                layout,
                capacity,
                offsets);

        return _C2BlockFactory::CreateGraphicBlock(alloc);
    }

    static constexpr uint8_t GetPixelValue(uint8_t value, uint32_t row, uint32_t col) {
        return (uint32_t(value) * row + col) & 0xFF;
    }

    static void FillPlane(C2GraphicView &view, size_t index, uint8_t value) {
        C2PlanarLayout layout = view.layout();

        uint8_t *rowPtr = view.data()[index];
        C2PlaneInfo plane = layout.planes[index];
        for (uint32_t row = 0; row < view.height() / plane.rowSampling; ++row) {
            uint8_t *colPtr = rowPtr;
            for (uint32_t col = 0; col < view.width() / plane.colSampling; ++col) {
                *colPtr = GetPixelValue(value, row, col);
                colPtr += plane.colInc;
            }
            rowPtr += plane.rowInc;
        }
    }

    static void FillBlock(const std::shared_ptr<C2GraphicBlock> &block) {
        C2GraphicView view = block->map().get();

        FillPlane(view, C2PlanarLayout::PLANE_Y, 'Y');
        FillPlane(view, C2PlanarLayout::PLANE_U, 'U');
        FillPlane(view, C2PlanarLayout::PLANE_V, 'V');
    }

    static bool VerifyPlane(
            const MediaImage2 *mediaImage,
            const uint8_t *base,
            uint32_t index,
            uint8_t value,
            std::string *errorMsg) {
        *errorMsg = "";
        MediaImage2::PlaneInfo plane = mediaImage->mPlane[index];
        const uint8_t *rowPtr = base + plane.mOffset;
        for (uint32_t row = 0; row < mediaImage->mHeight / plane.mVertSubsampling; ++row) {
            const uint8_t *colPtr = rowPtr;
            for (uint32_t col = 0; col < mediaImage->mWidth / plane.mHorizSubsampling; ++col) {
                if (GetPixelValue(value, row, col) != *colPtr) {
                    *errorMsg = AStringPrintf("row=%u col=%u expected=%02x actual=%02x",
                            row, col, GetPixelValue(value, row, col), *colPtr).c_str();
                    return false;
                }
                colPtr += plane.mColInc;
            }
            rowPtr += plane.mRowInc;
        }
        return true;
    }

public:
    static constexpr int32_t kWidth = 320;
    static constexpr int32_t kHeight = 240;
    static constexpr int32_t kGapLength = kWidth * kHeight * 10;

    static std::shared_ptr<C2Buffer> CreateAndFillBufferFromParam(const ParamType &param) {
        bool contiguous = std::get<0>(param);
        std::string planeOrderStr = std::get<1>(param);
        bool planar = std::get<2>(param);
        int32_t stride = std::get<3>(param);

        C2PlanarLayout::plane_index_t planeOrder[3];
        C2PlanarLayout layout;

        if (planeOrderStr.size() != 3) {
            return nullptr;
        }
        for (size_t i = 0; i < 3; ++i) {
            C2PlanarLayout::plane_index_t planeIndex;
            switch (planeOrderStr[i]) {
                case 'Y': planeIndex = C2PlanarLayout::PLANE_Y; break;
                case 'U': planeIndex = C2PlanarLayout::PLANE_U; break;
                case 'V': planeIndex = C2PlanarLayout::PLANE_V; break;
                default:  return nullptr;
            }
            planeOrder[i] = planeIndex;
        }

        if (planar) {
            layout = YUVPlanarLayout(stride);
        } else {  // semi-planar
            for (size_t i = 0; i < 3; ++i) {
                if (planeOrder[i] == C2PlanarLayout::PLANE_U) {
                    layout = YUVSemiPlanarLayout(stride);
                    break;
                }
                if (planeOrder[i] == C2PlanarLayout::PLANE_V) {
                    layout = YVUSemiPlanarLayout(stride);
                    break;
                }
            }
        }

        size_t yPlaneSize = stride * kHeight;
        size_t uvPlaneSize = stride * kHeight / 4;
        size_t capacity = yPlaneSize + uvPlaneSize * 2;
        std::vector<size_t> offsets(3);

        if (!contiguous) {
            if (planar) {
                capacity += kGapLength * 2;
            } else {  // semi-planar
                capacity += kGapLength;
            }
        }

        offsets[planeOrder[0]] = 0;
        size_t planeSize = (planeOrder[0] == C2PlanarLayout::PLANE_Y) ? yPlaneSize : uvPlaneSize;
        for (size_t i = 1; i < 3; ++i) {
            offsets[planeOrder[i]] = offsets[planeOrder[i - 1]] + planeSize;
            if (!contiguous) {
                offsets[planeOrder[i]] += kGapLength;
            }
            planeSize = (planeOrder[i] == C2PlanarLayout::PLANE_Y) ? yPlaneSize : uvPlaneSize;
            if (!planar  // semi-planar
                    && planeOrder[i - 1] != C2PlanarLayout::PLANE_Y
                    && planeOrder[i] != C2PlanarLayout::PLANE_Y) {
                offsets[planeOrder[i]] = offsets[planeOrder[i - 1]] + 1;
                planeSize = uvPlaneSize * 2 - 1;
            }
        }

        std::shared_ptr<C2GraphicBlock> block = CreateGraphicBlock(
                kWidth,
                kHeight,
                layout,
                capacity,
                offsets);
        FillBlock(block);
        return C2Buffer::CreateGraphicBuffer(
                block->share(block->crop(), C2Fence()));
    }

    static bool VerifyClientBuffer(
            const sp<MediaCodecBuffer> &buffer, std::string *errorMsg) {
        *errorMsg = "";
        sp<ABuffer> imageData;
        if (!buffer->format()->findBuffer("image-data", &imageData)) {
            *errorMsg = "Missing image data";
            return false;
        }
        MediaImage2 *mediaImage = (MediaImage2 *)imageData->data();
        if (mediaImage->mType != MediaImage2::MEDIA_IMAGE_TYPE_YUV) {
            *errorMsg = AStringPrintf("Unexpected type: %d", mediaImage->mType).c_str();
            return false;
        }
        std::string planeErrorMsg;
        if (!VerifyPlane(mediaImage, buffer->base(), MediaImage2::Y, 'Y', &planeErrorMsg)) {
            *errorMsg = "Y plane does not match: " + planeErrorMsg;
            return false;
        }
        if (!VerifyPlane(mediaImage, buffer->base(), MediaImage2::U, 'U', &planeErrorMsg)) {
            *errorMsg = "U plane does not match: " + planeErrorMsg;
            return false;
        }
        if (!VerifyPlane(mediaImage, buffer->base(), MediaImage2::V, 'V', &planeErrorMsg)) {
            *errorMsg = "V plane does not match: " + planeErrorMsg;
            return false;
        }

        int32_t width, height, stride;
        buffer->format()->findInt32(KEY_WIDTH, &width);
        buffer->format()->findInt32(KEY_HEIGHT, &height);
        buffer->format()->findInt32(KEY_STRIDE, &stride);

        MediaImage2 legacyYLayout = {
            MediaImage2::MEDIA_IMAGE_TYPE_Y,
            1,  // mNumPlanes
            uint32_t(width),
            uint32_t(height),
            8,
            8,
            {},  // mPlane
        };
        legacyYLayout.mPlane[MediaImage2::Y] = {
            0,  // mOffset
            1,  // mColInc
            stride,  // mRowInc
            1,  // mHorizSubsampling
            1,  // mVertSubsampling
        };
        if (!VerifyPlane(&legacyYLayout, buffer->data(), MediaImage2::Y, 'Y', &planeErrorMsg)) {
            *errorMsg = "Y plane by legacy layout does not match: " + planeErrorMsg;
            return false;
        }
        return true;
    }

};

TEST_P(LayoutTest, VerifyLayout) {
    std::shared_ptr<RawGraphicOutputBuffers> buffers =
        GetRawGraphicOutputBuffers(kWidth, kHeight);

    std::shared_ptr<C2Buffer> c2Buffer = CreateAndFillBufferFromParam(GetParam());
    ASSERT_NE(nullptr, c2Buffer);
    sp<MediaCodecBuffer> clientBuffer;
    size_t index;
    ASSERT_EQ(OK, buffers->registerBuffer(c2Buffer, &index, &clientBuffer));
    ASSERT_NE(nullptr, clientBuffer);
    std::string errorMsg;
    ASSERT_TRUE(VerifyClientBuffer(clientBuffer, &errorMsg)) << errorMsg;
}

INSTANTIATE_TEST_SUITE_P(
        RawGraphicOutputBuffersTest,
        LayoutTest,
        ::testing::Combine(
            ::testing::Bool(),  /* contiguous */
            ::testing::Values("YUV", "YVU", "UVY", "VUY"),
            ::testing::Bool(),  /* planar */
            ::testing::Values(320, 512)),
        [](const ::testing::TestParamInfo<LayoutTest::ParamType> &info) {
            std::string contiguous = std::get<0>(info.param) ? "Contiguous" : "Noncontiguous";
            std::string planar = std::get<2>(info.param) ? "Planar" : "SemiPlanar";
            return contiguous
                    + std::get<1>(info.param)
                    + planar
                    + std::to_string(std::get<3>(info.param));
        });

} // namespace android