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

Commit b1360ce2 authored by Fyodor Kyslov's avatar Fyodor Kyslov Committed by Android (Google) Code Review
Browse files

Merge "AV1 Encoder: Fix 10bit encoding"

parents 2ea68a82 d97e9913
Loading
Loading
Loading
Loading
+116 −63
Original line number Diff line number Diff line
@@ -104,6 +104,19 @@ C2SoftAomEnc::IntfImpl::IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helpe
                         .withSetter(ProfileLevelSetter)
                         .build());

    addParameter(DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
                         .withDefault(new C2StreamPixelFormatInfo::output(
                              0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
                         .withFields({C2F(mPixelFormat, value).oneOf({
                                            HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
                                            HAL_PIXEL_FORMAT_YCBCR_420_888,
                                            HAL_PIXEL_FORMAT_YCBCR_P010
                                     })
                         })
                         .withSetter((Setter<decltype(*mPixelFormat)>::StrictValueWithNoDeps))
                         .build());


    addParameter(DefineParam(mRequestSync, C2_PARAMKEY_REQUEST_SYNC_FRAME)
                         .withDefault(new C2StreamRequestSyncFrameTuning::output(0u, C2_FALSE))
                         .withFields({C2F(mRequestSync, value).oneOf({C2_FALSE, C2_TRUE})})
@@ -232,10 +245,11 @@ C2SoftAomEnc::C2SoftAomEnc(const char* name, c2_node_id_t id,
      mBitrateControlMode(AOM_VBR),
      mMinQuantizer(0),
      mMaxQuantizer(0),
      mLastTimestamp(0x7FFFFFFFFFFFFFFFull),
      mLastTimestamp(INT64_MAX),
      mSignalledOutputEos(false),
      mSignalledError(false),
      mHeadersReceived(false) {
      mHeadersReceived(false),
      mIs10Bit(false) {
    ALOGV("Constructor");
}

@@ -245,10 +259,7 @@ C2SoftAomEnc::~C2SoftAomEnc() {
}

c2_status_t C2SoftAomEnc::onInit() {
    ALOGV("Init");

    status_t err = initEncoder();
    return err == OK ? C2_OK : C2_CORRUPTED;
    return C2_OK;
}

c2_status_t C2SoftAomEnc::onStop() {
@@ -274,6 +285,7 @@ void C2SoftAomEnc::onRelease() {

    // this one is not allocated by us
    mCodecInterface = nullptr;
    mHeadersReceived = false;
}

c2_status_t C2SoftAomEnc::onFlush_sm() {
@@ -396,6 +408,7 @@ status_t C2SoftAomEnc::initEncoder() {
        mRequestSync = mIntf->getRequestSync_l();
    }


    switch (mBitrateMode->value) {
        case C2Config::BITRATE_CONST:
            mBitrateControlMode = AOM_CBR;
@@ -410,8 +423,9 @@ status_t C2SoftAomEnc::initEncoder() {
    mCodecInterface = aom_codec_av1_cx();
    if (!mCodecInterface) goto CleanUp;

    ALOGD("AOM: initEncoder. BRMode: %u. KF: %u. QP: %u - %u", (uint32_t)mBitrateControlMode,
          mIntf->getSyncFramePeriod(), mMinQuantizer, mMaxQuantizer);
    ALOGD("AOM: initEncoder. BRMode: %u. KF: %u. QP: %u - %u, 10Bit: %d",
          (uint32_t)mBitrateControlMode,
          mIntf->getSyncFramePeriod(), mMinQuantizer, mMaxQuantizer, mIs10Bit);

    mCodecConfiguration = new aom_codec_enc_cfg_t;
    if (!mCodecConfiguration) goto CleanUp;
@@ -425,6 +439,9 @@ status_t C2SoftAomEnc::initEncoder() {

    mCodecConfiguration->g_w = mSize->width;
    mCodecConfiguration->g_h = mSize->height;
    mCodecConfiguration->g_bit_depth = mIs10Bit ? AOM_BITS_10 : AOM_BITS_8;
    mCodecConfiguration->g_input_bit_depth = mIs10Bit ? 10 : 8;


    mCodecConfiguration->g_threads = 0;
    mCodecConfiguration->g_error_resilient = 0;
@@ -489,7 +506,7 @@ status_t C2SoftAomEnc::initEncoder() {
    mCodecContext = new aom_codec_ctx_t;
    if (!mCodecContext) goto CleanUp;
    codec_return = aom_codec_enc_init(mCodecContext, mCodecInterface, mCodecConfiguration,
                                      0);  // flags
                                      mIs10Bit ? AOM_CODEC_USE_HIGHBITDEPTH : 0);
    if (codec_return != AOM_CODEC_OK) {
        ALOGE("Error initializing aom encoder");
        goto CleanUp;
@@ -511,7 +528,7 @@ status_t C2SoftAomEnc::initEncoder() {
        } else {
            uint32_t stride = (width + mStrideAlign - 1) & ~(mStrideAlign - 1);
            uint32_t vstride = (height + mStrideAlign - 1) & ~(mStrideAlign - 1);
            mConversionBuffer = MemoryBlock::Allocate(stride * vstride * 3 / 2);
            mConversionBuffer = MemoryBlock::Allocate(stride * vstride * 3 / (mIs10Bit? 1 : 2));
            if (!mConversionBuffer.size()) {
                ALOGE("Allocating conversion buffer failed.");
            } else {
@@ -537,7 +554,42 @@ void C2SoftAomEnc::process(const std::unique_ptr<C2Work>& work,
        work->result = C2_BAD_VALUE;
        return;
    }
    // Initialize encoder if not already

    std::shared_ptr<const C2GraphicView> rView;
    std::shared_ptr<C2Buffer> inputBuffer;
    if (!work->input.buffers.empty()) {
        inputBuffer = work->input.buffers[0];
        rView = std::make_shared<const C2GraphicView>(
                inputBuffer->data().graphicBlocks().front().map().get());
        if (rView->error() != C2_OK) {
            ALOGE("graphic view map err = %d", rView->error());
            work->result = C2_CORRUPTED;
            return;
        }
    } else {
        ALOGV("Empty input Buffer");
        uint32_t flags = 0;
        if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
            flags |= C2FrameData::FLAG_END_OF_STREAM;
        }
        work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
        work->worklets.front()->output.buffers.clear();
        work->worklets.front()->output.ordinal = work->input.ordinal;
        work->workletsProcessed = 1u;
        return;
    }

    bool end_of_stream = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
    aom_image_t raw_frame;
    const C2PlanarLayout& layout = rView->layout();
    if (!mHeadersReceived) {
        mIs10Bit = (layout.planes[layout.PLANE_Y].bitDepth == 10);

        // Re-Initialize encoder
        if (mCodecContext){
            onRelease();
        }
    }
    if (!mCodecContext && OK != initEncoder()) {
        ALOGE("Failed to initialize encoder");
        mSignalledError = true;
@@ -587,30 +639,6 @@ void C2SoftAomEnc::process(const std::unique_ptr<C2Work>& work,
        ALOGV("CSD Produced of size %zu bytes", header_bytes);
    }

    std::shared_ptr<const C2GraphicView> rView;
    std::shared_ptr<C2Buffer> inputBuffer;
    if (!work->input.buffers.empty()) {
        inputBuffer = work->input.buffers[0];
        rView = std::make_shared<const C2GraphicView>(
                inputBuffer->data().graphicBlocks().front().map().get());
        if (rView->error() != C2_OK) {
            ALOGE("graphic view map err = %d", rView->error());
            work->result = C2_CORRUPTED;
            return;
        }
    } else {
        ALOGV("Empty input Buffer");
        uint32_t flags = 0;
        if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
            flags |= C2FrameData::FLAG_END_OF_STREAM;
        }
        work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
        work->worklets.front()->output.buffers.clear();
        work->worklets.front()->output.ordinal = work->input.ordinal;
        work->workletsProcessed = 1u;
        return;
    }

    const C2ConstGraphicBlock inBuffer = inputBuffer->data().graphicBlocks().front();
    if (inBuffer.width() < mSize->width || inBuffer.height() < mSize->height) {
        ALOGE("unexpected Input buffer attributes %d(%d) x %d(%d)", inBuffer.width(), mSize->width,
@@ -619,9 +647,8 @@ void C2SoftAomEnc::process(const std::unique_ptr<C2Work>& work,
        work->result = C2_BAD_VALUE;
        return;
    }
    bool end_of_stream = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
    aom_image_t raw_frame;
    const C2PlanarLayout& layout = rView->layout();


    uint32_t width = mSize->width;
    uint32_t height = mSize->height;
    if (width > 0x8000 || height > 0x8000) {
@@ -647,15 +674,14 @@ void C2SoftAomEnc::process(const std::unique_ptr<C2Work>& work,
            break;
        }
        case C2PlanarLayout::TYPE_YUV: {
            if (!IsYUV420(*rView)) {
            const bool isYUV420_10bit = IsYUV420_10bit(*rView);
            if (!IsYUV420(*rView) && !isYUV420_10bit) {
                ALOGE("input is not YUV420");
                work->result = C2_BAD_VALUE;
                return;
            }

            if (layout.planes[layout.PLANE_Y].colInc == 1 &&
                layout.planes[layout.PLANE_U].colInc == 1 &&
                layout.planes[layout.PLANE_V].colInc == 1) {
            if (!isYUV420_10bit) {
                if (IsI420(*rView)) {
                    // I420 compatible - though with custom offset and stride
                    aom_img_wrap(&raw_frame, AOM_IMG_FMT_I420, width, height, mStrideAlign,
                                 (uint8_t*)rView->data()[0]);
@@ -665,6 +691,7 @@ void C2SoftAomEnc::process(const std::unique_ptr<C2Work>& work,
                    raw_frame.stride[1] = layout.planes[layout.PLANE_U].rowInc;
                    raw_frame.stride[2] = layout.planes[layout.PLANE_V].rowInc;
                } else {
                    // TODO(kyslov): Add image wrap for NV12
                    // copy to I420
                    MediaImage2 img = CreateYUV420PlanarMediaImage2(width, height, stride, vstride);
                    if (mConversionBuffer.size() >= stride * vstride * 3 / 2) {
@@ -684,6 +711,32 @@ void C2SoftAomEnc::process(const std::unique_ptr<C2Work>& work,
                        return;
                    }
                }
            } else {  // 10 bits
                if (IsP010(*rView)) {
                    if (mConversionBuffer.size() >= stride * vstride * 3) {
                        uint16_t *dstY, *dstU, *dstV;
                        dstY = (uint16_t*)mConversionBuffer.data();
                        dstU = ((uint16_t*)mConversionBuffer.data()) + stride * vstride;
                        dstV = ((uint16_t*)mConversionBuffer.data()) + (stride * vstride) / 4;
                        convertP010ToYUV420Planar16(dstY, dstU, dstV, (uint16_t*)(rView->data()[0]),
                                                    (uint16_t*)(rView->data()[1]), stride, stride,
                                                    stride, stride / 2, stride / 2, stride,
                                                    vstride);
                        aom_img_wrap(&raw_frame, AOM_IMG_FMT_I42016, stride, vstride, mStrideAlign,
                                     mConversionBuffer.data());
                        aom_img_set_rect(&raw_frame, 0, 0, width, height, 0);
                    } else {
                        ALOGE("Conversion buffer is too small: %u x %u for %zu", stride, vstride,
                              mConversionBuffer.size());
                        work->result = C2_BAD_VALUE;
                        return;
                    }
                } else {
                    ALOGE("Image format conversion is not supported.");
                    work->result = C2_BAD_VALUE;
                    return;
                }
            }
            break;
        }
        default:
+7 −0
Original line number Diff line number Diff line
@@ -96,6 +96,8 @@ struct C2SoftAomEnc : public SimpleC2Component {

    bool mHeadersReceived;

    bool mIs10Bit;

    std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
    std::shared_ptr<C2StreamIntraRefreshTuning::output> mIntraRefresh;
    std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
@@ -133,6 +135,9 @@ class C2SoftAomEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
    std::shared_ptr<C2StreamColorAspectsInfo::output> getCodedColorAspects_l() const {
        return mCodedColorAspects;
    }
    std::shared_ptr<C2StreamPixelFormatInfo::output> getPixelFormat_l() const {
        return mPixelFormat;
    }
    uint32_t getSyncFramePeriod() const;
    static C2R ColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::input>& me);
    static C2R CodedColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::output>& me,
@@ -150,6 +155,8 @@ class C2SoftAomEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
    std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
    std::shared_ptr<C2StreamColorAspectsInfo::input> mColorAspects;
    std::shared_ptr<C2StreamColorAspectsInfo::output> mCodedColorAspects;
    std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormat;

};

}  // namespace android
+38 −0
Original line number Diff line number Diff line
@@ -414,6 +414,44 @@ void convertYUV420Planar16ToP010(uint16_t *dstY, uint16_t *dstUV, const uint16_t
        dstUV += dstUVStride;
    }
}

void convertP010ToYUV420Planar16(uint16_t *dstY, uint16_t *dstU, uint16_t *dstV,
                                 const uint16_t *srcY, const uint16_t *srcUV,
                                 size_t srcYStride, size_t srcUVStride, size_t dstYStride,
                                 size_t dstUStride, size_t dstVStride, size_t width,
                                 size_t height, bool isMonochrome) {
    for (size_t y = 0; y < height; ++y) {
        for (size_t x = 0; x < width; ++x) {
            dstY[x] = srcY[x] >> 6;
        }
        srcY += srcYStride;
        dstY += dstYStride;
    }

    if (isMonochrome) {
        // Fill with neutral U/V values.
        for (size_t y = 0; y < (height + 1) / 2; ++y) {
            for (size_t x = 0; x < (width + 1) / 2; ++x) {
                dstU[x] = kNeutralUVBitDepth10;
                dstV[x] = kNeutralUVBitDepth10;
            }
            dstU += dstUStride;
            dstV += dstVStride;
        }
        return;
    }

    for (size_t y = 0; y < (height + 1) / 2; ++y) {
        for (size_t x = 0; x < (width + 1) / 2; ++x) {
            dstU[x] = srcUV[2 * x] >> 6;
            dstV[x] = srcUV[2 * x + 1] >> 6;
        }
        dstU += dstUStride;
        dstV += dstVStride;
        srcUV += srcUVStride;
    }
}

std::unique_ptr<C2Work> SimpleC2Component::WorkQueue::pop_front() {
    std::unique_ptr<C2Work> work = std::move(mQueue.front().work);
    mQueue.pop_front();
+6 −0
Original line number Diff line number Diff line
@@ -55,6 +55,12 @@ void convertYUV420Planar16ToP010(uint16_t *dstY, uint16_t *dstUV, const uint16_t
                                 size_t dstUVStride, size_t width, size_t height,
                                 bool isMonochrome = false);

void convertP010ToYUV420Planar16(uint16_t *dstY, uint16_t *dstU, uint16_t *dstV,
                                 const uint16_t *srcY, const uint16_t *srcUV,
                                 size_t srcYStride, size_t srcUVStride, size_t dstYStride,
                                 size_t dstUStride, size_t dstVStride, size_t width,
                                 size_t height, bool isMonochrome = false);

class SimpleC2Component
        : public C2Component, public std::enable_shared_from_this<SimpleC2Component> {
public:
+40 −0
Original line number Diff line number Diff line
@@ -313,6 +313,28 @@ bool IsYUV420(const C2GraphicView &view) {
            && layout.planes[layout.PLANE_V].rowSampling == 2);
}

bool IsYUV420_10bit(const C2GraphicView &view) {
    const C2PlanarLayout &layout = view.layout();
    return (layout.numPlanes == 3
            && layout.type == C2PlanarLayout::TYPE_YUV
            && layout.planes[layout.PLANE_Y].channel == C2PlaneInfo::CHANNEL_Y
            && layout.planes[layout.PLANE_Y].allocatedDepth == 16
            && layout.planes[layout.PLANE_Y].bitDepth == 10
            && layout.planes[layout.PLANE_Y].colSampling == 1
            && layout.planes[layout.PLANE_Y].rowSampling == 1
            && layout.planes[layout.PLANE_U].channel == C2PlaneInfo::CHANNEL_CB
            && layout.planes[layout.PLANE_U].allocatedDepth == 16
            && layout.planes[layout.PLANE_U].bitDepth == 10
            && layout.planes[layout.PLANE_U].colSampling == 2
            && layout.planes[layout.PLANE_U].rowSampling == 2
            && layout.planes[layout.PLANE_V].channel == C2PlaneInfo::CHANNEL_CR
            && layout.planes[layout.PLANE_V].allocatedDepth == 16
            && layout.planes[layout.PLANE_V].bitDepth == 10
            && layout.planes[layout.PLANE_V].colSampling == 2
            && layout.planes[layout.PLANE_V].rowSampling == 2);
}


bool IsNV12(const C2GraphicView &view) {
    if (!IsYUV420(view)) {
        return false;
@@ -327,6 +349,24 @@ bool IsNV12(const C2GraphicView &view) {
            && layout.planes[layout.PLANE_V].offset == 1);
}

bool IsP010(const C2GraphicView &view) {
    if (!IsYUV420_10bit(view)) {
        return false;
    }
    const C2PlanarLayout &layout = view.layout();
    return (layout.rootPlanes == 2
            && layout.planes[layout.PLANE_U].colInc == 4
            && layout.planes[layout.PLANE_U].rootIx == layout.PLANE_U
            && layout.planes[layout.PLANE_U].offset == 0
            && layout.planes[layout.PLANE_V].colInc == 4
            && layout.planes[layout.PLANE_V].rootIx == layout.PLANE_U
            && layout.planes[layout.PLANE_V].offset == 2
            && layout.planes[layout.PLANE_Y].rightShift == 6
            && layout.planes[layout.PLANE_U].rightShift == 6
            && layout.planes[layout.PLANE_V].rightShift == 6);
}


bool IsNV21(const C2GraphicView &view) {
    if (!IsYUV420(view)) {
        return false;
Loading