Implement AV1 Encoder via Codec2 interface
This implementes AV1 Encoder via Codec2 interface. The encoder supports
8 and 10 bit input, Dynamic BR change, Forced key frame. No B-Frame
support at the moment.
Bug:246859000
Test: atest VideoCodecTest
Change-Id: I2c2d5dc2804f996e6c2fb1b8469a63072dec64ea
diff --git a/media/codec2/components/aom/Android.bp b/media/codec2/components/aom/Android.bp
index a2a79d5..257cf4e 100644
--- a/media/codec2/components/aom/Android.bp
+++ b/media/codec2/components/aom/Android.bp
@@ -23,3 +23,23 @@
srcs: ["C2SoftAomDec.cpp"],
static_libs: ["libaom"],
}
+
+cc_library {
+ name: "libcodec2_soft_av1enc",
+ defaults: [
+ "libcodec2_soft-defaults",
+ "libcodec2_soft_sanitize_all-defaults",
+ ],
+
+ static_libs: ["libaom"],
+
+ srcs: ["C2SoftAomEnc.cpp"],
+
+ export_include_dirs: ["."],
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media.swcodec",
+ ],
+
+}
diff --git a/media/codec2/components/aom/C2SoftAomEnc.cpp b/media/codec2/components/aom/C2SoftAomEnc.cpp
new file mode 100644
index 0000000..362d75c
--- /dev/null
+++ b/media/codec2/components/aom/C2SoftAomEnc.cpp
@@ -0,0 +1,866 @@
+/*
+ * Copyright (C) 2022 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 "C2SoftAomEnc"
+#include <log/log.h>
+
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftAomEnc.h"
+
+namespace android {
+
+constexpr char COMPONENT_NAME[] = "c2.android.av1.encoder";
+
+#define DEFAULT_SPEED 10
+
+C2SoftAomEnc::IntfImpl::IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
+ : SimpleInterface<void>::BaseParams(helper, COMPONENT_NAME, C2Component::KIND_ENCODER,
+ C2Component::DOMAIN_VIDEO, MEDIA_MIMETYPE_VIDEO_AV1) {
+ noPrivateBuffers(); // TODO: account for our buffers here
+ noInputReferences();
+ noOutputReferences();
+ noInputLatency();
+ noTimeStretch();
+ setDerivedInstance(this);
+
+ addParameter(DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
+ .withConstValue(new C2StreamUsageTuning::input(
+ 0u, (uint64_t)C2MemoryUsage::CPU_READ))
+ .build());
+
+ addParameter(DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+ .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240))
+ .withFields({
+ C2F(mSize, width).inRange(2, 2048, 2),
+ C2F(mSize, height).inRange(2, 2048, 2),
+ })
+ .withSetter(SizeSetter)
+ .build());
+
+ addParameter(DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE)
+ .withDefault(new C2StreamBitrateModeTuning::output(
+ 0u, C2Config::BITRATE_VARIABLE))
+ .withFields({C2F(mBitrateMode, value)
+ .oneOf({C2Config::BITRATE_CONST,
+ C2Config::BITRATE_VARIABLE})})
+ .withSetter(Setter<decltype(*mBitrateMode)>::StrictValueWithNoDeps)
+ .build());
+
+ addParameter(DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
+ .withDefault(new C2StreamFrameRateInfo::output(0u, 30.))
+ // TODO: More restriction?
+ .withFields({C2F(mFrameRate, value).greaterThan(0.)})
+ .withSetter(Setter<decltype(*mFrameRate)>::StrictValueWithNoDeps)
+ .build());
+
+ addParameter(DefineParam(mSyncFramePeriod, C2_PARAMKEY_SYNC_FRAME_INTERVAL)
+ .withDefault(new C2StreamSyncFrameIntervalTuning::output(0u, 1000000))
+ .withFields({C2F(mSyncFramePeriod, value).any()})
+ .withSetter(Setter<decltype(*mSyncFramePeriod)>::StrictValueWithNoDeps)
+ .build());
+
+ addParameter(DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+ .withDefault(new C2StreamBitrateInfo::output(0u, 64000))
+ .withFields({C2F(mBitrate, value).inRange(4096, 40000000)})
+ .withSetter(BitrateSetter)
+ .build());
+
+ addParameter(DefineParam(mIntraRefresh, C2_PARAMKEY_INTRA_REFRESH)
+ .withConstValue(new C2StreamIntraRefreshTuning::output(
+ 0u, C2Config::INTRA_REFRESH_DISABLED, 0.))
+ .build());
+
+ addParameter(DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+ .withDefault(new C2StreamProfileLevelInfo::output(0u, PROFILE_AV1_0,
+ LEVEL_AV1_4_1))
+ .withFields({
+ C2F(mProfileLevel, profile).equalTo(PROFILE_AV1_0),
+ C2F(mProfileLevel, level).equalTo(LEVEL_AV1_4_1),
+ })
+ .withSetter(ProfileLevelSetter)
+ .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})})
+ .withSetter(Setter<decltype(*mRequestSync)>::NonStrictValueWithNoDeps)
+ .build());
+ addParameter(
+ DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
+ .withDefault(new C2StreamColorAspectsInfo::input(
+ 0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+ .withFields(
+ {C2F(mColorAspects, range)
+ .inRange(C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
+ C2F(mColorAspects, primaries)
+ .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::PRIMARIES_OTHER),
+ C2F(mColorAspects, transfer)
+ .inRange(C2Color::TRANSFER_UNSPECIFIED,
+ C2Color::TRANSFER_OTHER),
+ C2F(mColorAspects, matrix)
+ .inRange(C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)})
+ .withSetter(ColorAspectsSetter)
+ .build());
+
+ addParameter(
+ DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
+ .withDefault(new C2StreamColorAspectsInfo::output(
+ 0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+ .withFields(
+ {C2F(mCodedColorAspects, range)
+ .inRange(C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
+ C2F(mCodedColorAspects, primaries)
+ .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::PRIMARIES_OTHER),
+ C2F(mCodedColorAspects, transfer)
+ .inRange(C2Color::TRANSFER_UNSPECIFIED,
+ C2Color::TRANSFER_OTHER),
+ C2F(mCodedColorAspects, matrix)
+ .inRange(C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)})
+ .withSetter(CodedColorAspectsSetter, mColorAspects)
+ .build());
+}
+
+C2R C2SoftAomEnc::IntfImpl::BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output>& me) {
+ (void)mayBlock;
+ C2R res = C2R::Ok();
+ if (me.v.value < 4096) {
+ me.set().value = 4096;
+ }
+ return res;
+}
+
+C2R C2SoftAomEnc::IntfImpl::SizeSetter(bool mayBlock,
+ const C2P<C2StreamPictureSizeInfo::input>& oldMe,
+ C2P<C2StreamPictureSizeInfo::input>& me) {
+ (void)mayBlock;
+ C2R res = C2R::Ok();
+ if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+ res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+ me.set().width = oldMe.v.width;
+ }
+ if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+ res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+ me.set().height = oldMe.v.height;
+ }
+ return res;
+}
+
+C2R C2SoftAomEnc::IntfImpl::ProfileLevelSetter(bool mayBlock,
+ C2P<C2StreamProfileLevelInfo::output>& me) {
+ (void)mayBlock;
+ if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
+ me.set().profile = PROFILE_AV1_0;
+ }
+ if (!me.F(me.v.level).supportsAtAll(me.v.level)) {
+ me.set().level = LEVEL_AV1_4_1;
+ }
+ return C2R::Ok();
+}
+
+uint32_t C2SoftAomEnc::IntfImpl::getSyncFramePeriod() const {
+ if (mSyncFramePeriod->value < 0 || mSyncFramePeriod->value == INT64_MAX) {
+ return 0;
+ }
+ double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value;
+ return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.);
+}
+
+C2R C2SoftAomEnc::IntfImpl::ColorAspectsSetter(bool mayBlock,
+ C2P<C2StreamColorAspectsInfo::input>& me) {
+ (void)mayBlock;
+ if (me.v.range > C2Color::RANGE_OTHER) {
+ me.set().range = C2Color::RANGE_OTHER;
+ }
+ if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+ me.set().primaries = C2Color::PRIMARIES_OTHER;
+ }
+ if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+ me.set().transfer = C2Color::TRANSFER_OTHER;
+ }
+ if (me.v.matrix > C2Color::MATRIX_OTHER) {
+ me.set().matrix = C2Color::MATRIX_OTHER;
+ }
+ return C2R::Ok();
+}
+C2R C2SoftAomEnc::IntfImpl::CodedColorAspectsSetter(
+ bool mayBlock, C2P<C2StreamColorAspectsInfo::output>& me,
+ const C2P<C2StreamColorAspectsInfo::input>& coded) {
+ (void)mayBlock;
+ me.set().range = coded.v.range;
+ me.set().primaries = coded.v.primaries;
+ me.set().transfer = coded.v.transfer;
+ me.set().matrix = coded.v.matrix;
+ return C2R::Ok();
+}
+
+C2SoftAomEnc::C2SoftAomEnc(const char* name, c2_node_id_t id,
+ const std::shared_ptr<IntfImpl>& intfImpl)
+ : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+ mIntf(intfImpl),
+ mCodecContext(nullptr),
+ mCodecConfiguration(nullptr),
+ mCodecInterface(nullptr),
+ mStrideAlign(2),
+ mBitrateControlMode(AOM_VBR),
+ mMinQuantizer(0),
+ mMaxQuantizer(0),
+ mLastTimestamp(0x7FFFFFFFFFFFFFFFull),
+ mSignalledOutputEos(false),
+ mSignalledError(false),
+ mHeadersReceived(false) {
+ ALOGV("Constructor");
+}
+
+C2SoftAomEnc::~C2SoftAomEnc() {
+ ALOGV("Destructor");
+ onRelease();
+}
+
+c2_status_t C2SoftAomEnc::onInit() {
+ ALOGV("Init");
+
+ status_t err = initEncoder();
+ return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftAomEnc::onStop() {
+ onRelease();
+ return C2_OK;
+}
+
+void C2SoftAomEnc::onReset() {
+ (void)onStop();
+}
+
+void C2SoftAomEnc::onRelease() {
+ if (mCodecContext) {
+ aom_codec_destroy(mCodecContext);
+ delete mCodecContext;
+ mCodecContext = nullptr;
+ }
+
+ if (mCodecConfiguration) {
+ delete mCodecConfiguration;
+ mCodecConfiguration = nullptr;
+ }
+
+ // this one is not allocated by us
+ mCodecInterface = nullptr;
+}
+
+c2_status_t C2SoftAomEnc::onFlush_sm() {
+ return onStop();
+}
+
+aom_codec_err_t C2SoftAomEnc::setupCodecParameters() {
+ aom_codec_err_t codec_return = AOM_CODEC_OK;
+
+ codec_return = aom_codec_control(mCodecContext, AOME_SET_CPUUSED, DEFAULT_SPEED);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ROW_MT, 1);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_CDEF, 1);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_TPL_MODEL, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_DELTAQ_MODE, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_ORDER_HINT, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_AQ_MODE, 3);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_COEFF_COST_UPD_FREQ, 3);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_MODE_COST_UPD_FREQ, 3);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_MV_COST_UPD_FREQ, 3);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_PALETTE, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_OBMC, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_NOISE_SENSITIVITY, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_WARPED_MOTION, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_GLOBAL_MOTION, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_REF_FRAME_MVS, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_CFL_INTRA, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_SMOOTH_INTRA, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_ANGLE_DELTA, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_FILTER_INTRA, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_INTRA_DEFAULT_TX_ONLY, 1);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_DISABLE_TRELLIS_QUANT, 1);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_DIST_WTD_COMP, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_DIFF_WTD_COMP, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_DUAL_FILTER, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_INTERINTRA_COMP, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_INTERINTRA_WEDGE, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_INTRA_EDGE_FILTER, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_INTRABC, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_MASKED_COMP, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_PAETH_INTRA, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_QM, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_RECT_PARTITIONS, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_RESTORATION, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_SMOOTH_INTERINTRA, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_TX64, 0);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+ codec_return = aom_codec_control(mCodecContext, AV1E_SET_MAX_REFERENCE_FRAMES, 3);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
+BailOut:
+ return codec_return;
+}
+
+status_t C2SoftAomEnc::initEncoder() {
+ aom_codec_err_t codec_return;
+ status_t result = UNKNOWN_ERROR;
+ {
+ IntfImpl::Lock lock = mIntf->lock();
+ // Fetch config
+ mSize = mIntf->getSize_l();
+ mBitrate = mIntf->getBitrate_l();
+ mBitrateMode = mIntf->getBitrateMode_l();
+ mFrameRate = mIntf->getFrameRate_l();
+ mIntraRefresh = mIntf->getIntraRefresh_l();
+ mRequestSync = mIntf->getRequestSync_l();
+ }
+
+ switch (mBitrateMode->value) {
+ case C2Config::BITRATE_CONST:
+ mBitrateControlMode = AOM_CBR;
+ break;
+ case C2Config::BITRATE_VARIABLE:
+ [[fallthrough]];
+ default:
+ mBitrateControlMode = AOM_VBR;
+ break;
+ }
+
+ 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);
+
+ mCodecConfiguration = new aom_codec_enc_cfg_t;
+ if (!mCodecConfiguration) goto CleanUp;
+
+ codec_return = aom_codec_enc_config_default(mCodecInterface, mCodecConfiguration,
+ AOM_USAGE_REALTIME); // RT mode
+ if (codec_return != AOM_CODEC_OK) {
+ ALOGE("Error populating default configuration for aom encoder.");
+ goto CleanUp;
+ }
+
+ mCodecConfiguration->g_w = mSize->width;
+ mCodecConfiguration->g_h = mSize->height;
+
+ mCodecConfiguration->g_threads = 0;
+ mCodecConfiguration->g_error_resilient = 0;
+
+ // timebase unit is microsecond
+ // g_timebase is in seconds (i.e. 1/1000000 seconds)
+ mCodecConfiguration->g_timebase.num = 1;
+ mCodecConfiguration->g_timebase.den = 1000000;
+ // rc_target_bitrate is in kbps, mBitrate in bps
+ mCodecConfiguration->rc_target_bitrate = (mBitrate->value + 500) / 1000;
+ mCodecConfiguration->rc_end_usage = AOM_CBR;
+ // Disable frame drop - not allowed in MediaCodec now.
+ mCodecConfiguration->rc_dropframe_thresh = 0;
+ // Disable lagged encoding.
+ mCodecConfiguration->g_lag_in_frames = 0;
+
+ // Disable spatial resizing.
+ mCodecConfiguration->rc_resize_mode = 0;
+ // Single-pass mode.
+ mCodecConfiguration->g_pass = AOM_RC_ONE_PASS;
+
+ // Maximum key frame interval - for CBR boost to 3000
+ mCodecConfiguration->kf_max_dist = 3000;
+ // Encoder determines optimal key frame placement automatically.
+ mCodecConfiguration->kf_mode = AOM_KF_AUTO;
+ // Initial value of the buffer level in ms.
+ mCodecConfiguration->rc_buf_initial_sz = 500;
+ // Amount of data that the encoder should try to maintain in ms.
+ mCodecConfiguration->rc_buf_optimal_sz = 600;
+ // The amount of data that may be buffered by the decoding
+ // application in ms.
+ mCodecConfiguration->rc_buf_sz = 1000;
+
+ if (mBitrateControlMode == AOM_CBR) {
+ // Maximum amount of bits that can be subtracted from the target
+ // bitrate - expressed as percentage of the target bitrate.
+ mCodecConfiguration->rc_undershoot_pct = 100;
+ // Maximum amount of bits that can be added to the target
+ // bitrate - expressed as percentage of the target bitrate.
+ mCodecConfiguration->rc_overshoot_pct = 10;
+ } else {
+ // Maximum amount of bits that can be subtracted from the target
+ // bitrate - expressed as percentage of the target bitrate.
+ mCodecConfiguration->rc_undershoot_pct = 100;
+ // Maximum amount of bits that can be added to the target
+ // bitrate - expressed as percentage of the target bitrate.
+ mCodecConfiguration->rc_overshoot_pct = 25;
+ }
+
+ if (mIntf->getSyncFramePeriod() >= 0) {
+ mCodecConfiguration->kf_max_dist = mIntf->getSyncFramePeriod();
+ mCodecConfiguration->kf_min_dist = mIntf->getSyncFramePeriod();
+ mCodecConfiguration->kf_mode = AOM_KF_AUTO;
+ }
+ if (mMinQuantizer > 0) {
+ mCodecConfiguration->rc_min_quantizer = mMinQuantizer;
+ }
+ if (mMaxQuantizer > 0) {
+ mCodecConfiguration->rc_max_quantizer = mMaxQuantizer;
+ }
+
+ mCodecContext = new aom_codec_ctx_t;
+ if (!mCodecContext) goto CleanUp;
+ codec_return = aom_codec_enc_init(mCodecContext, mCodecInterface, mCodecConfiguration,
+ 0); // flags
+ if (codec_return != AOM_CODEC_OK) {
+ ALOGE("Error initializing aom encoder");
+ goto CleanUp;
+ }
+
+ codec_return = setupCodecParameters();
+ if (codec_return != AOM_CODEC_OK) {
+ ALOGE("Error setting up codec parameters");
+ goto CleanUp;
+ }
+
+ mHeadersReceived = false;
+
+ {
+ uint32_t width = mSize->width;
+ uint32_t height = mSize->height;
+ if (((uint64_t)width * height) > ((uint64_t)INT32_MAX / 3)) {
+ ALOGE("b/25812794, Buffer size is too big, width=%u, height=%u.", width, height);
+ } else {
+ uint32_t stride = (width + mStrideAlign - 1) & ~(mStrideAlign - 1);
+ uint32_t vstride = (height + mStrideAlign - 1) & ~(mStrideAlign - 1);
+ mConversionBuffer = MemoryBlock::Allocate(stride * vstride * 3 / 2);
+ if (!mConversionBuffer.size()) {
+ ALOGE("Allocating conversion buffer failed.");
+ } else {
+ mNumInputFrames = -1;
+ return OK;
+ }
+ }
+ }
+
+CleanUp:
+ onRelease();
+ return result;
+}
+
+void C2SoftAomEnc::process(const std::unique_ptr<C2Work>& work,
+ const std::shared_ptr<C2BlockPool>& pool) {
+ // Initialize output work
+ work->result = C2_OK;
+ work->workletsProcessed = 1u;
+ work->worklets.front()->output.flags = work->input.flags;
+
+ if (mSignalledError || mSignalledOutputEos) {
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+ // Initialize encoder if not already
+ if (!mCodecContext && OK != initEncoder()) {
+ ALOGE("Failed to initialize encoder");
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ if (!mHeadersReceived) {
+ Av1Config av1_config;
+ constexpr uint32_t header_length = 2048;
+ uint8_t header[header_length];
+ size_t header_bytes;
+ aom_fixed_buf_t* obu_sequence_header = aom_codec_get_global_headers(mCodecContext);
+ int ret = 1;
+ if (obu_sequence_header) {
+ if (get_av1config_from_obu(reinterpret_cast<const uint8_t*>(obu_sequence_header->buf),
+ obu_sequence_header->sz, false, &av1_config) == 0) {
+ ret = write_av1config(&av1_config, header_length, &header_bytes, header);
+
+ } else {
+ ALOGE("Can not get config");
+ }
+ free(obu_sequence_header->buf);
+ free(obu_sequence_header);
+ }
+
+ if (ret) {
+ ALOGE("Can not write config");
+ mSignalledError = true;
+ work->result = C2_NO_MEMORY;
+ work->workletsProcessed = 1u;
+ return;
+ }
+
+ mHeadersReceived = true;
+ std::unique_ptr<C2StreamInitDataInfo::output> csd =
+ C2StreamInitDataInfo::output::AllocUnique(header_bytes, 0u);
+ if (!csd) {
+ ALOGE("CSD allocation failed");
+ mSignalledError = true;
+ work->result = C2_NO_MEMORY;
+ work->workletsProcessed = 1u;
+ return;
+ }
+ memcpy(csd->m.value, header, header_bytes);
+ work->worklets.front()->output.configUpdate.push_back(std::move(csd));
+ 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,
+ inBuffer.height(), mSize->height);
+ mSignalledError = true;
+ 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) {
+ ALOGE("Image too big: %u x %u", width, height);
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+ uint32_t stride = (width + mStrideAlign - 1) & ~(mStrideAlign - 1);
+ uint32_t vstride = (height + mStrideAlign - 1) & ~(mStrideAlign - 1);
+ switch (layout.type) {
+ case C2PlanarLayout::TYPE_RGB:
+ case C2PlanarLayout::TYPE_RGBA: {
+ std::shared_ptr<C2StreamColorAspectsInfo::output> colorAspects;
+ {
+ IntfImpl::Lock lock = mIntf->lock();
+ colorAspects = mIntf->getCodedColorAspects_l();
+ }
+ ConvertRGBToPlanarYUV(mConversionBuffer.data(), stride, vstride,
+ mConversionBuffer.size(), *rView.get(), colorAspects->matrix,
+ colorAspects->range);
+ aom_img_wrap(&raw_frame, AOM_IMG_FMT_I420, width, height, mStrideAlign,
+ mConversionBuffer.data());
+ break;
+ }
+ case C2PlanarLayout::TYPE_YUV: {
+ if (!IsYUV420(*rView)) {
+ 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) {
+ // 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]);
+ raw_frame.planes[1] = (uint8_t*)rView->data()[1];
+ raw_frame.planes[2] = (uint8_t*)rView->data()[2];
+ raw_frame.stride[0] = layout.planes[layout.PLANE_Y].rowInc;
+ raw_frame.stride[1] = layout.planes[layout.PLANE_U].rowInc;
+ raw_frame.stride[2] = layout.planes[layout.PLANE_V].rowInc;
+ } else {
+ // copy to I420
+ MediaImage2 img = CreateYUV420PlanarMediaImage2(width, height, stride, vstride);
+ if (mConversionBuffer.size() >= stride * vstride * 3 / 2) {
+ status_t err = ImageCopy(mConversionBuffer.data(), &img, *rView);
+ if (err != OK) {
+ ALOGE("Buffer conversion failed: %d", err);
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+ aom_img_wrap(&raw_frame, AOM_IMG_FMT_I420, 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;
+ }
+ }
+ break;
+ }
+ default:
+ ALOGE("Unrecognized plane type: %d", layout.type);
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+
+ aom_enc_frame_flags_t flags = 0;
+ // handle dynamic config parameters
+ {
+ IntfImpl::Lock lock = mIntf->lock();
+ std::shared_ptr<C2StreamIntraRefreshTuning::output> intraRefresh =
+ mIntf->getIntraRefresh_l();
+ std::shared_ptr<C2StreamBitrateInfo::output> bitrate = mIntf->getBitrate_l();
+ std::shared_ptr<C2StreamRequestSyncFrameTuning::output> requestSync =
+ mIntf->getRequestSync_l();
+ lock.unlock();
+
+ if (intraRefresh != mIntraRefresh) {
+ mIntraRefresh = intraRefresh;
+ ALOGV("Got mIntraRefresh request");
+ }
+
+ if (requestSync != mRequestSync) {
+ // we can handle IDR immediately
+ if (requestSync->value) {
+ // unset request
+ C2StreamRequestSyncFrameTuning::output clearSync(0u, C2_FALSE);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ mIntf->config({&clearSync}, C2_MAY_BLOCK, &failures);
+ ALOGV("Got sync request");
+ flags |= AOM_EFLAG_FORCE_KF;
+ }
+ mRequestSync = requestSync;
+ }
+
+ if (bitrate != mBitrate) {
+ mBitrate = bitrate;
+ mCodecConfiguration->rc_target_bitrate = (mBitrate->value + 500) / 1000;
+ aom_codec_err_t res = aom_codec_enc_config_set(mCodecContext, mCodecConfiguration);
+ if (res != AOM_CODEC_OK) {
+ ALOGE("aom encoder failed to update bitrate: %s", aom_codec_err_to_string(res));
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ }
+ }
+
+ uint64_t input_timestamp = work->input.ordinal.timestamp.peekull();
+ uint32_t frame_duration;
+ if (input_timestamp > mLastTimestamp) {
+ frame_duration = (uint32_t)(input_timestamp - mLastTimestamp);
+ } else {
+ // Use default of 30 fps in case of 0 frame rate.
+ float frame_rate = mFrameRate->value;
+ if (frame_rate < 0.001) {
+ frame_rate = 30.0;
+ }
+ frame_duration = (uint32_t)(1000000 / frame_rate + 0.5);
+ }
+ mLastTimestamp = input_timestamp;
+
+ aom_codec_err_t codec_return =
+ aom_codec_encode(mCodecContext, &raw_frame, input_timestamp, frame_duration, flags);
+ if (codec_return != AOM_CODEC_OK) {
+ ALOGE("aom encoder failed to encode frame");
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ bool populated = false;
+ aom_codec_iter_t encoded_packet_iterator = nullptr;
+ const aom_codec_cx_pkt_t* encoded_packet;
+ while ((encoded_packet = aom_codec_get_cx_data(mCodecContext, &encoded_packet_iterator))) {
+ if (encoded_packet->kind == AOM_CODEC_CX_FRAME_PKT) {
+ std::shared_ptr<C2LinearBlock> block;
+ C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+ c2_status_t err = pool->fetchLinearBlock(encoded_packet->data.frame.sz, usage, &block);
+ if (err != C2_OK) {
+ ALOGE("fetchLinearBlock for Output failed with status %d", err);
+ work->result = C2_NO_MEMORY;
+ return;
+ }
+ C2WriteView wView = block->map().get();
+ if (wView.error()) {
+ ALOGE("write view map failed %d", wView.error());
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ memcpy(wView.data(), encoded_packet->data.frame.buf, encoded_packet->data.frame.sz);
+ ++mNumInputFrames;
+
+ ALOGD("bytes generated %zu", encoded_packet->data.frame.sz);
+ uint32_t flags = 0;
+ if (end_of_stream) {
+ flags |= C2FrameData::FLAG_END_OF_STREAM;
+ }
+
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+ work->worklets.front()->output.buffers.clear();
+ std::shared_ptr<C2Buffer> buffer =
+ createLinearBuffer(block, 0, encoded_packet->data.frame.sz);
+ if (encoded_packet->data.frame.flags & AOM_FRAME_IS_KEY) {
+ buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
+ 0u /* stream id */, C2Config::SYNC_FRAME));
+ }
+ work->worklets.front()->output.buffers.push_back(buffer);
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->worklets.front()->output.ordinal.timestamp = encoded_packet->data.frame.pts;
+ work->workletsProcessed = 1u;
+ populated = true;
+ if (end_of_stream) {
+ mSignalledOutputEos = true;
+ ALOGV("signalled End Of Stream");
+ }
+ }
+ }
+ if (!populated) {
+ work->workletsProcessed = 0u;
+ }
+}
+
+c2_status_t C2SoftAomEnc::drain(uint32_t drainMode, const std::shared_ptr<C2BlockPool>& pool) {
+ (void)pool;
+ if (drainMode == NO_DRAIN) {
+ ALOGW("drain with NO_DRAIN: no-op");
+ return C2_OK;
+ }
+ if (drainMode == DRAIN_CHAIN) {
+ ALOGW("DRAIN_CHAIN not supported");
+ return C2_OMITTED;
+ }
+
+ return C2_OK;
+}
+
+class C2SoftAomEncFactory : public C2ComponentFactory {
+ public:
+ C2SoftAomEncFactory()
+ : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+ GetCodec2PlatformComponentStore()->getParamReflector())) {}
+
+ virtual c2_status_t createComponent(c2_node_id_t id,
+ std::shared_ptr<C2Component>* const component,
+ std::function<void(C2Component*)> deleter) override {
+ *component = std::shared_ptr<C2Component>(
+ new C2SoftAomEnc(COMPONENT_NAME, id,
+ std::make_shared<C2SoftAomEnc::IntfImpl>(mHelper)),
+ deleter);
+ return C2_OK;
+ }
+
+ virtual c2_status_t createInterface(
+ c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
+ std::function<void(C2ComponentInterface*)> deleter) override {
+ *interface = std::shared_ptr<C2ComponentInterface>(
+ new SimpleInterface<C2SoftAomEnc::IntfImpl>(
+ COMPONENT_NAME, id, std::make_shared<C2SoftAomEnc::IntfImpl>(mHelper)),
+ deleter);
+ return C2_OK;
+ }
+
+ virtual ~C2SoftAomEncFactory() override = default;
+
+ private:
+ std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+} // namespace android
+
+__attribute__((cfi_canonical_jump_table)) extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+ ALOGV("in %s", __func__);
+ return new ::android::C2SoftAomEncFactory();
+}
+
+__attribute__((cfi_canonical_jump_table)) extern "C" void DestroyCodec2Factory(
+ ::C2ComponentFactory* factory) {
+ ALOGV("in %s", __func__);
+ delete factory;
+}
diff --git a/media/codec2/components/aom/C2SoftAomEnc.h b/media/codec2/components/aom/C2SoftAomEnc.h
new file mode 100644
index 0000000..4b820ea
--- /dev/null
+++ b/media/codec2/components/aom/C2SoftAomEnc.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef ANDROID_C2_SOFT_AV1_ENC_H_
+#define ANDROID_C2_SOFT_AV1_ENC_H_
+
+#include <inttypes.h>
+
+#include <C2PlatformSupport.h>
+#include <Codec2BufferUtils.h>
+#include <SimpleC2Component.h>
+#include <SimpleC2Interface.h>
+#include <util/C2InterfaceHelper.h>
+
+#include "aom/aom_encoder.h"
+#include "aom/aomcx.h"
+#include "common/av1_config.h"
+
+namespace android {
+struct C2SoftAomEnc : public SimpleC2Component {
+ class IntfImpl;
+
+ C2SoftAomEnc(const char* name, c2_node_id_t id, const std::shared_ptr<IntfImpl>& intfImpl);
+
+ // From SimpleC2Component
+ c2_status_t onInit() override final;
+ c2_status_t onStop() override final;
+ void onReset() override final;
+ void onRelease() override final;
+ c2_status_t onFlush_sm() override final;
+
+ void process(const std::unique_ptr<C2Work>& work,
+ const std::shared_ptr<C2BlockPool>& pool) override final;
+ c2_status_t drain(uint32_t drainMode, const std::shared_ptr<C2BlockPool>& pool) override final;
+
+protected:
+ virtual ~C2SoftAomEnc();
+
+private:
+ std::shared_ptr<IntfImpl> mIntf;
+
+ // Initializes aom encoder with available settings.
+ status_t initEncoder();
+
+ // aom specific opaque data structure that
+ // stores encoder state
+ aom_codec_ctx_t* mCodecContext;
+
+ // aom specific data structure that
+ // stores encoder configuration
+ aom_codec_enc_cfg_t* mCodecConfiguration;
+
+ // aom specific read-only data structure
+ // that specifies algorithm interface
+ aom_codec_iface_t* mCodecInterface;
+
+ // align stride to the power of 2
+ int32_t mStrideAlign;
+
+
+ aom_rc_mode mBitrateControlMode;
+
+ // Minimum (best quality) quantizer
+ uint32_t mMinQuantizer;
+
+ // Maximum (worst quality) quantizer
+ uint32_t mMaxQuantizer;
+
+ // Last input buffer timestamp
+ uint64_t mLastTimestamp;
+
+ // Number of input frames
+ int64_t mNumInputFrames;
+
+ // Conversion buffer is needed to input to
+ // yuv420 planar format.
+ MemoryBlock mConversionBuffer;
+
+ // Signalled End Of Stream
+ bool mSignalledOutputEos;
+
+ // Signalled Error
+ bool mSignalledError;
+
+ bool mHeadersReceived;
+
+ std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
+ std::shared_ptr<C2StreamIntraRefreshTuning::output> mIntraRefresh;
+ std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
+ std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+ std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
+ std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
+
+ aom_codec_err_t setupCodecParameters();
+};
+
+class C2SoftAomEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+ explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper);
+
+ static C2R BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output> &me);
+
+ static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::input> &oldMe,
+ C2P<C2StreamPictureSizeInfo::input> &me);
+
+ static C2R ProfileLevelSetter(
+ bool mayBlock,
+ C2P<C2StreamProfileLevelInfo::output> &me);
+
+
+ // unsafe getters
+ std::shared_ptr<C2StreamPictureSizeInfo::input> getSize_l() const { return mSize; }
+ std::shared_ptr<C2StreamIntraRefreshTuning::output> getIntraRefresh_l() const {
+ return mIntraRefresh;
+ }
+ std::shared_ptr<C2StreamFrameRateInfo::output> getFrameRate_l() const { return mFrameRate; }
+ std::shared_ptr<C2StreamBitrateInfo::output> getBitrate_l() const { return mBitrate; }
+ std::shared_ptr<C2StreamBitrateModeTuning::output> getBitrateMode_l() const {
+ return mBitrateMode;
+ }
+ std::shared_ptr<C2StreamRequestSyncFrameTuning::output> getRequestSync_l() const {
+ return mRequestSync;
+ }
+ std::shared_ptr<C2StreamColorAspectsInfo::output> getCodedColorAspects_l() const {
+ return mCodedColorAspects;
+ }
+ uint32_t getSyncFramePeriod() const;
+ static C2R ColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::input> &me);
+ static C2R CodedColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::output> &me,
+ const C2P<C2StreamColorAspectsInfo::input> &coded);
+
+private:
+ std::shared_ptr<C2StreamUsageTuning::input> mUsage;
+ std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
+ std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
+ std::shared_ptr<C2StreamIntraRefreshTuning::output> mIntraRefresh;
+ std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
+ std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod;
+ std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+ std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
+ std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
+ std::shared_ptr<C2StreamColorAspectsInfo::input> mColorAspects;
+ std::shared_ptr<C2StreamColorAspectsInfo::output> mCodedColorAspects;
+};
+
+} // namespace android
+#endif // ANDROID_C2_SOFT_AV1_ENC_H_