blob: f49c1c49a2d53f8084cb1f77c9a2354712e295eb [file] [log] [blame]
Fyodor Kyslovadc71142022-09-15 16:47:03 +00001/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "C2SoftAomEnc"
19#include <log/log.h>
20
21#include <media/stagefright/foundation/AUtils.h>
22#include <media/stagefright/foundation/MediaDefs.h>
23
24#include <C2Debug.h>
25#include <C2PlatformSupport.h>
26#include <SimpleC2Interface.h>
27
28#include "C2SoftAomEnc.h"
29
30namespace android {
31
32constexpr char COMPONENT_NAME[] = "c2.android.av1.encoder";
33
34#define DEFAULT_SPEED 10
35
36C2SoftAomEnc::IntfImpl::IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
37 : SimpleInterface<void>::BaseParams(helper, COMPONENT_NAME, C2Component::KIND_ENCODER,
38 C2Component::DOMAIN_VIDEO, MEDIA_MIMETYPE_VIDEO_AV1) {
39 noPrivateBuffers(); // TODO: account for our buffers here
40 noInputReferences();
41 noOutputReferences();
42 noInputLatency();
43 noTimeStretch();
44 setDerivedInstance(this);
45
46 addParameter(DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
47 .withConstValue(new C2StreamUsageTuning::input(
48 0u, (uint64_t)C2MemoryUsage::CPU_READ))
49 .build());
50
51 addParameter(DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
52 .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240))
53 .withFields({
54 C2F(mSize, width).inRange(2, 2048, 2),
55 C2F(mSize, height).inRange(2, 2048, 2),
56 })
57 .withSetter(SizeSetter)
58 .build());
59
60 addParameter(DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE)
61 .withDefault(new C2StreamBitrateModeTuning::output(
62 0u, C2Config::BITRATE_VARIABLE))
63 .withFields({C2F(mBitrateMode, value)
64 .oneOf({C2Config::BITRATE_CONST,
65 C2Config::BITRATE_VARIABLE})})
66 .withSetter(Setter<decltype(*mBitrateMode)>::StrictValueWithNoDeps)
67 .build());
68
69 addParameter(DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
70 .withDefault(new C2StreamFrameRateInfo::output(0u, 30.))
71 // TODO: More restriction?
72 .withFields({C2F(mFrameRate, value).greaterThan(0.)})
73 .withSetter(Setter<decltype(*mFrameRate)>::StrictValueWithNoDeps)
74 .build());
75
76 addParameter(DefineParam(mSyncFramePeriod, C2_PARAMKEY_SYNC_FRAME_INTERVAL)
77 .withDefault(new C2StreamSyncFrameIntervalTuning::output(0u, 1000000))
78 .withFields({C2F(mSyncFramePeriod, value).any()})
79 .withSetter(Setter<decltype(*mSyncFramePeriod)>::StrictValueWithNoDeps)
80 .build());
81
82 addParameter(DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
83 .withDefault(new C2StreamBitrateInfo::output(0u, 64000))
84 .withFields({C2F(mBitrate, value).inRange(4096, 40000000)})
85 .withSetter(BitrateSetter)
86 .build());
87
88 addParameter(DefineParam(mIntraRefresh, C2_PARAMKEY_INTRA_REFRESH)
89 .withConstValue(new C2StreamIntraRefreshTuning::output(
90 0u, C2Config::INTRA_REFRESH_DISABLED, 0.))
91 .build());
92
93 addParameter(DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
94 .withDefault(new C2StreamProfileLevelInfo::output(0u, PROFILE_AV1_0,
95 LEVEL_AV1_4_1))
96 .withFields({
97 C2F(mProfileLevel, profile).equalTo(PROFILE_AV1_0),
Fyodor Kyslova8092322022-10-09 02:47:33 +000098 C2F(mProfileLevel, level)
99 .oneOf({LEVEL_AV1_2, LEVEL_AV1_2_1, LEVEL_AV1_2_2,
100 LEVEL_AV1_2_3, LEVEL_AV1_3, LEVEL_AV1_3_1,
101 LEVEL_AV1_3_2, LEVEL_AV1_3_3, LEVEL_AV1_4,
102 LEVEL_AV1_4_1}),
Fyodor Kyslovadc71142022-09-15 16:47:03 +0000103 })
104 .withSetter(ProfileLevelSetter)
105 .build());
106
107 addParameter(DefineParam(mRequestSync, C2_PARAMKEY_REQUEST_SYNC_FRAME)
108 .withDefault(new C2StreamRequestSyncFrameTuning::output(0u, C2_FALSE))
109 .withFields({C2F(mRequestSync, value).oneOf({C2_FALSE, C2_TRUE})})
110 .withSetter(Setter<decltype(*mRequestSync)>::NonStrictValueWithNoDeps)
111 .build());
112 addParameter(
113 DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
114 .withDefault(new C2StreamColorAspectsInfo::input(
115 0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
116 C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
117 .withFields(
118 {C2F(mColorAspects, range)
119 .inRange(C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
120 C2F(mColorAspects, primaries)
121 .inRange(C2Color::PRIMARIES_UNSPECIFIED,
122 C2Color::PRIMARIES_OTHER),
123 C2F(mColorAspects, transfer)
124 .inRange(C2Color::TRANSFER_UNSPECIFIED,
125 C2Color::TRANSFER_OTHER),
126 C2F(mColorAspects, matrix)
127 .inRange(C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)})
128 .withSetter(ColorAspectsSetter)
129 .build());
130
131 addParameter(
132 DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
133 .withDefault(new C2StreamColorAspectsInfo::output(
134 0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
135 C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
136 .withFields(
137 {C2F(mCodedColorAspects, range)
138 .inRange(C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
139 C2F(mCodedColorAspects, primaries)
140 .inRange(C2Color::PRIMARIES_UNSPECIFIED,
141 C2Color::PRIMARIES_OTHER),
142 C2F(mCodedColorAspects, transfer)
143 .inRange(C2Color::TRANSFER_UNSPECIFIED,
144 C2Color::TRANSFER_OTHER),
145 C2F(mCodedColorAspects, matrix)
146 .inRange(C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)})
147 .withSetter(CodedColorAspectsSetter, mColorAspects)
148 .build());
149}
150
151C2R C2SoftAomEnc::IntfImpl::BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output>& me) {
152 (void)mayBlock;
153 C2R res = C2R::Ok();
154 if (me.v.value < 4096) {
155 me.set().value = 4096;
156 }
157 return res;
158}
159
160C2R C2SoftAomEnc::IntfImpl::SizeSetter(bool mayBlock,
161 const C2P<C2StreamPictureSizeInfo::input>& oldMe,
162 C2P<C2StreamPictureSizeInfo::input>& me) {
163 (void)mayBlock;
164 C2R res = C2R::Ok();
165 if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
166 res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
167 me.set().width = oldMe.v.width;
168 }
169 if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
170 res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
171 me.set().height = oldMe.v.height;
172 }
173 return res;
174}
175
176C2R C2SoftAomEnc::IntfImpl::ProfileLevelSetter(bool mayBlock,
177 C2P<C2StreamProfileLevelInfo::output>& me) {
178 (void)mayBlock;
179 if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
180 me.set().profile = PROFILE_AV1_0;
181 }
182 if (!me.F(me.v.level).supportsAtAll(me.v.level)) {
183 me.set().level = LEVEL_AV1_4_1;
184 }
185 return C2R::Ok();
186}
187
188uint32_t C2SoftAomEnc::IntfImpl::getSyncFramePeriod() const {
189 if (mSyncFramePeriod->value < 0 || mSyncFramePeriod->value == INT64_MAX) {
190 return 0;
191 }
192 double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value;
193 return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.);
194}
195
196C2R C2SoftAomEnc::IntfImpl::ColorAspectsSetter(bool mayBlock,
197 C2P<C2StreamColorAspectsInfo::input>& me) {
198 (void)mayBlock;
199 if (me.v.range > C2Color::RANGE_OTHER) {
200 me.set().range = C2Color::RANGE_OTHER;
201 }
202 if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
203 me.set().primaries = C2Color::PRIMARIES_OTHER;
204 }
205 if (me.v.transfer > C2Color::TRANSFER_OTHER) {
206 me.set().transfer = C2Color::TRANSFER_OTHER;
207 }
208 if (me.v.matrix > C2Color::MATRIX_OTHER) {
209 me.set().matrix = C2Color::MATRIX_OTHER;
210 }
211 return C2R::Ok();
212}
213C2R C2SoftAomEnc::IntfImpl::CodedColorAspectsSetter(
214 bool mayBlock, C2P<C2StreamColorAspectsInfo::output>& me,
215 const C2P<C2StreamColorAspectsInfo::input>& coded) {
216 (void)mayBlock;
217 me.set().range = coded.v.range;
218 me.set().primaries = coded.v.primaries;
219 me.set().transfer = coded.v.transfer;
220 me.set().matrix = coded.v.matrix;
221 return C2R::Ok();
222}
223
224C2SoftAomEnc::C2SoftAomEnc(const char* name, c2_node_id_t id,
225 const std::shared_ptr<IntfImpl>& intfImpl)
226 : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
227 mIntf(intfImpl),
228 mCodecContext(nullptr),
229 mCodecConfiguration(nullptr),
230 mCodecInterface(nullptr),
231 mStrideAlign(2),
232 mBitrateControlMode(AOM_VBR),
233 mMinQuantizer(0),
234 mMaxQuantizer(0),
235 mLastTimestamp(0x7FFFFFFFFFFFFFFFull),
236 mSignalledOutputEos(false),
237 mSignalledError(false),
238 mHeadersReceived(false) {
239 ALOGV("Constructor");
240}
241
242C2SoftAomEnc::~C2SoftAomEnc() {
243 ALOGV("Destructor");
244 onRelease();
245}
246
247c2_status_t C2SoftAomEnc::onInit() {
248 ALOGV("Init");
249
250 status_t err = initEncoder();
251 return err == OK ? C2_OK : C2_CORRUPTED;
252}
253
254c2_status_t C2SoftAomEnc::onStop() {
255 onRelease();
256 return C2_OK;
257}
258
259void C2SoftAomEnc::onReset() {
260 (void)onStop();
261}
262
263void C2SoftAomEnc::onRelease() {
264 if (mCodecContext) {
265 aom_codec_destroy(mCodecContext);
266 delete mCodecContext;
267 mCodecContext = nullptr;
268 }
269
270 if (mCodecConfiguration) {
271 delete mCodecConfiguration;
272 mCodecConfiguration = nullptr;
273 }
274
275 // this one is not allocated by us
276 mCodecInterface = nullptr;
277}
278
279c2_status_t C2SoftAomEnc::onFlush_sm() {
280 return onStop();
281}
282
283aom_codec_err_t C2SoftAomEnc::setupCodecParameters() {
284 aom_codec_err_t codec_return = AOM_CODEC_OK;
285
286 codec_return = aom_codec_control(mCodecContext, AOME_SET_CPUUSED, DEFAULT_SPEED);
287 if (codec_return != AOM_CODEC_OK) goto BailOut;
288
289 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ROW_MT, 1);
290 if (codec_return != AOM_CODEC_OK) goto BailOut;
291
292 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_CDEF, 1);
293 if (codec_return != AOM_CODEC_OK) goto BailOut;
294
295 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_TPL_MODEL, 0);
296 if (codec_return != AOM_CODEC_OK) goto BailOut;
297
298 codec_return = aom_codec_control(mCodecContext, AV1E_SET_DELTAQ_MODE, 0);
299 if (codec_return != AOM_CODEC_OK) goto BailOut;
300
301 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_ORDER_HINT, 0);
302 if (codec_return != AOM_CODEC_OK) goto BailOut;
303
304 codec_return = aom_codec_control(mCodecContext, AV1E_SET_AQ_MODE, 3);
305 if (codec_return != AOM_CODEC_OK) goto BailOut;
306
307 codec_return = aom_codec_control(mCodecContext, AV1E_SET_COEFF_COST_UPD_FREQ, 3);
308 if (codec_return != AOM_CODEC_OK) goto BailOut;
309
310 codec_return = aom_codec_control(mCodecContext, AV1E_SET_MODE_COST_UPD_FREQ, 3);
311 if (codec_return != AOM_CODEC_OK) goto BailOut;
312
313 codec_return = aom_codec_control(mCodecContext, AV1E_SET_MV_COST_UPD_FREQ, 3);
314 if (codec_return != AOM_CODEC_OK) goto BailOut;
315
316 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_PALETTE, 0);
317 if (codec_return != AOM_CODEC_OK) goto BailOut;
318
319 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_OBMC, 0);
320 if (codec_return != AOM_CODEC_OK) goto BailOut;
321
322 codec_return = aom_codec_control(mCodecContext, AV1E_SET_NOISE_SENSITIVITY, 0);
323 if (codec_return != AOM_CODEC_OK) goto BailOut;
324
325 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_WARPED_MOTION, 0);
326 if (codec_return != AOM_CODEC_OK) goto BailOut;
327
328 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_GLOBAL_MOTION, 0);
329 if (codec_return != AOM_CODEC_OK) goto BailOut;
330
331 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_REF_FRAME_MVS, 0);
332 if (codec_return != AOM_CODEC_OK) goto BailOut;
333
334 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_CFL_INTRA, 0);
335 if (codec_return != AOM_CODEC_OK) goto BailOut;
336
337 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_SMOOTH_INTRA, 0);
338 if (codec_return != AOM_CODEC_OK) goto BailOut;
339 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_ANGLE_DELTA, 0);
340 if (codec_return != AOM_CODEC_OK) goto BailOut;
341 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_FILTER_INTRA, 0);
342 if (codec_return != AOM_CODEC_OK) goto BailOut;
343
344 codec_return = aom_codec_control(mCodecContext, AV1E_SET_INTRA_DEFAULT_TX_ONLY, 1);
345 if (codec_return != AOM_CODEC_OK) goto BailOut;
346 codec_return = aom_codec_control(mCodecContext, AV1E_SET_DISABLE_TRELLIS_QUANT, 1);
347 if (codec_return != AOM_CODEC_OK) goto BailOut;
348
349 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_DIST_WTD_COMP, 0);
350 if (codec_return != AOM_CODEC_OK) goto BailOut;
351 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_DIFF_WTD_COMP, 0);
352 if (codec_return != AOM_CODEC_OK) goto BailOut;
353 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_DUAL_FILTER, 0);
354 if (codec_return != AOM_CODEC_OK) goto BailOut;
355 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_INTERINTRA_COMP, 0);
356 if (codec_return != AOM_CODEC_OK) goto BailOut;
357 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_INTERINTRA_WEDGE, 0);
358 if (codec_return != AOM_CODEC_OK) goto BailOut;
359 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_INTRA_EDGE_FILTER, 0);
360 if (codec_return != AOM_CODEC_OK) goto BailOut;
361 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_INTRABC, 0);
362 if (codec_return != AOM_CODEC_OK) goto BailOut;
363 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_MASKED_COMP, 0);
364 if (codec_return != AOM_CODEC_OK) goto BailOut;
365 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_PAETH_INTRA, 0);
366 if (codec_return != AOM_CODEC_OK) goto BailOut;
367 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_QM, 0);
368 if (codec_return != AOM_CODEC_OK) goto BailOut;
369 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_RECT_PARTITIONS, 0);
370 if (codec_return != AOM_CODEC_OK) goto BailOut;
371 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_RESTORATION, 0);
372 if (codec_return != AOM_CODEC_OK) goto BailOut;
373 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_SMOOTH_INTERINTRA, 0);
374 if (codec_return != AOM_CODEC_OK) goto BailOut;
375 codec_return = aom_codec_control(mCodecContext, AV1E_SET_ENABLE_TX64, 0);
376 if (codec_return != AOM_CODEC_OK) goto BailOut;
377
378 codec_return = aom_codec_control(mCodecContext, AV1E_SET_MAX_REFERENCE_FRAMES, 3);
379 if (codec_return != AOM_CODEC_OK) goto BailOut;
380
381BailOut:
382 return codec_return;
383}
384
385status_t C2SoftAomEnc::initEncoder() {
386 aom_codec_err_t codec_return;
387 status_t result = UNKNOWN_ERROR;
388 {
389 IntfImpl::Lock lock = mIntf->lock();
390 // Fetch config
391 mSize = mIntf->getSize_l();
392 mBitrate = mIntf->getBitrate_l();
393 mBitrateMode = mIntf->getBitrateMode_l();
394 mFrameRate = mIntf->getFrameRate_l();
395 mIntraRefresh = mIntf->getIntraRefresh_l();
396 mRequestSync = mIntf->getRequestSync_l();
397 }
398
399 switch (mBitrateMode->value) {
400 case C2Config::BITRATE_CONST:
401 mBitrateControlMode = AOM_CBR;
402 break;
403 case C2Config::BITRATE_VARIABLE:
404 [[fallthrough]];
405 default:
406 mBitrateControlMode = AOM_VBR;
407 break;
408 }
409
410 mCodecInterface = aom_codec_av1_cx();
411 if (!mCodecInterface) goto CleanUp;
412
413 ALOGD("AOM: initEncoder. BRMode: %u. KF: %u. QP: %u - %u", (uint32_t)mBitrateControlMode,
414 mIntf->getSyncFramePeriod(), mMinQuantizer, mMaxQuantizer);
415
416 mCodecConfiguration = new aom_codec_enc_cfg_t;
417 if (!mCodecConfiguration) goto CleanUp;
418
419 codec_return = aom_codec_enc_config_default(mCodecInterface, mCodecConfiguration,
420 AOM_USAGE_REALTIME); // RT mode
421 if (codec_return != AOM_CODEC_OK) {
422 ALOGE("Error populating default configuration for aom encoder.");
423 goto CleanUp;
424 }
425
426 mCodecConfiguration->g_w = mSize->width;
427 mCodecConfiguration->g_h = mSize->height;
428
429 mCodecConfiguration->g_threads = 0;
430 mCodecConfiguration->g_error_resilient = 0;
431
432 // timebase unit is microsecond
433 // g_timebase is in seconds (i.e. 1/1000000 seconds)
434 mCodecConfiguration->g_timebase.num = 1;
435 mCodecConfiguration->g_timebase.den = 1000000;
436 // rc_target_bitrate is in kbps, mBitrate in bps
437 mCodecConfiguration->rc_target_bitrate = (mBitrate->value + 500) / 1000;
438 mCodecConfiguration->rc_end_usage = AOM_CBR;
439 // Disable frame drop - not allowed in MediaCodec now.
440 mCodecConfiguration->rc_dropframe_thresh = 0;
441 // Disable lagged encoding.
442 mCodecConfiguration->g_lag_in_frames = 0;
443
444 // Disable spatial resizing.
445 mCodecConfiguration->rc_resize_mode = 0;
446 // Single-pass mode.
447 mCodecConfiguration->g_pass = AOM_RC_ONE_PASS;
448
449 // Maximum key frame interval - for CBR boost to 3000
450 mCodecConfiguration->kf_max_dist = 3000;
451 // Encoder determines optimal key frame placement automatically.
452 mCodecConfiguration->kf_mode = AOM_KF_AUTO;
453 // Initial value of the buffer level in ms.
454 mCodecConfiguration->rc_buf_initial_sz = 500;
455 // Amount of data that the encoder should try to maintain in ms.
456 mCodecConfiguration->rc_buf_optimal_sz = 600;
457 // The amount of data that may be buffered by the decoding
458 // application in ms.
459 mCodecConfiguration->rc_buf_sz = 1000;
460
461 if (mBitrateControlMode == AOM_CBR) {
462 // Maximum amount of bits that can be subtracted from the target
463 // bitrate - expressed as percentage of the target bitrate.
464 mCodecConfiguration->rc_undershoot_pct = 100;
465 // Maximum amount of bits that can be added to the target
466 // bitrate - expressed as percentage of the target bitrate.
467 mCodecConfiguration->rc_overshoot_pct = 10;
468 } else {
469 // Maximum amount of bits that can be subtracted from the target
470 // bitrate - expressed as percentage of the target bitrate.
471 mCodecConfiguration->rc_undershoot_pct = 100;
472 // Maximum amount of bits that can be added to the target
473 // bitrate - expressed as percentage of the target bitrate.
474 mCodecConfiguration->rc_overshoot_pct = 25;
475 }
476
477 if (mIntf->getSyncFramePeriod() >= 0) {
478 mCodecConfiguration->kf_max_dist = mIntf->getSyncFramePeriod();
479 mCodecConfiguration->kf_min_dist = mIntf->getSyncFramePeriod();
480 mCodecConfiguration->kf_mode = AOM_KF_AUTO;
481 }
482 if (mMinQuantizer > 0) {
483 mCodecConfiguration->rc_min_quantizer = mMinQuantizer;
484 }
485 if (mMaxQuantizer > 0) {
486 mCodecConfiguration->rc_max_quantizer = mMaxQuantizer;
487 }
488
489 mCodecContext = new aom_codec_ctx_t;
490 if (!mCodecContext) goto CleanUp;
491 codec_return = aom_codec_enc_init(mCodecContext, mCodecInterface, mCodecConfiguration,
492 0); // flags
493 if (codec_return != AOM_CODEC_OK) {
494 ALOGE("Error initializing aom encoder");
495 goto CleanUp;
496 }
497
498 codec_return = setupCodecParameters();
499 if (codec_return != AOM_CODEC_OK) {
500 ALOGE("Error setting up codec parameters");
501 goto CleanUp;
502 }
503
504 mHeadersReceived = false;
505
506 {
507 uint32_t width = mSize->width;
508 uint32_t height = mSize->height;
509 if (((uint64_t)width * height) > ((uint64_t)INT32_MAX / 3)) {
510 ALOGE("b/25812794, Buffer size is too big, width=%u, height=%u.", width, height);
511 } else {
512 uint32_t stride = (width + mStrideAlign - 1) & ~(mStrideAlign - 1);
513 uint32_t vstride = (height + mStrideAlign - 1) & ~(mStrideAlign - 1);
514 mConversionBuffer = MemoryBlock::Allocate(stride * vstride * 3 / 2);
515 if (!mConversionBuffer.size()) {
516 ALOGE("Allocating conversion buffer failed.");
517 } else {
518 mNumInputFrames = -1;
519 return OK;
520 }
521 }
522 }
523
524CleanUp:
525 onRelease();
526 return result;
527}
528
529void C2SoftAomEnc::process(const std::unique_ptr<C2Work>& work,
530 const std::shared_ptr<C2BlockPool>& pool) {
531 // Initialize output work
532 work->result = C2_OK;
533 work->workletsProcessed = 1u;
534 work->worklets.front()->output.flags = work->input.flags;
535
536 if (mSignalledError || mSignalledOutputEos) {
537 work->result = C2_BAD_VALUE;
538 return;
539 }
540 // Initialize encoder if not already
541 if (!mCodecContext && OK != initEncoder()) {
542 ALOGE("Failed to initialize encoder");
543 mSignalledError = true;
544 work->result = C2_CORRUPTED;
545 return;
546 }
547
548 if (!mHeadersReceived) {
549 Av1Config av1_config;
550 constexpr uint32_t header_length = 2048;
551 uint8_t header[header_length];
552 size_t header_bytes;
553 aom_fixed_buf_t* obu_sequence_header = aom_codec_get_global_headers(mCodecContext);
554 int ret = 1;
555 if (obu_sequence_header) {
556 if (get_av1config_from_obu(reinterpret_cast<const uint8_t*>(obu_sequence_header->buf),
557 obu_sequence_header->sz, false, &av1_config) == 0) {
558 ret = write_av1config(&av1_config, header_length, &header_bytes, header);
559
560 } else {
561 ALOGE("Can not get config");
562 }
563 free(obu_sequence_header->buf);
564 free(obu_sequence_header);
565 }
566
567 if (ret) {
568 ALOGE("Can not write config");
569 mSignalledError = true;
570 work->result = C2_NO_MEMORY;
571 work->workletsProcessed = 1u;
572 return;
573 }
574
575 mHeadersReceived = true;
576 std::unique_ptr<C2StreamInitDataInfo::output> csd =
577 C2StreamInitDataInfo::output::AllocUnique(header_bytes, 0u);
578 if (!csd) {
579 ALOGE("CSD allocation failed");
580 mSignalledError = true;
581 work->result = C2_NO_MEMORY;
582 work->workletsProcessed = 1u;
583 return;
584 }
585 memcpy(csd->m.value, header, header_bytes);
586 work->worklets.front()->output.configUpdate.push_back(std::move(csd));
587 ALOGV("CSD Produced of size %zu bytes", header_bytes);
588 }
589
590 std::shared_ptr<const C2GraphicView> rView;
591 std::shared_ptr<C2Buffer> inputBuffer;
592 if (!work->input.buffers.empty()) {
593 inputBuffer = work->input.buffers[0];
594 rView = std::make_shared<const C2GraphicView>(
595 inputBuffer->data().graphicBlocks().front().map().get());
596 if (rView->error() != C2_OK) {
597 ALOGE("graphic view map err = %d", rView->error());
598 work->result = C2_CORRUPTED;
599 return;
600 }
601 } else {
602 ALOGV("Empty input Buffer");
603 uint32_t flags = 0;
604 if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
605 flags |= C2FrameData::FLAG_END_OF_STREAM;
606 }
607 work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
608 work->worklets.front()->output.buffers.clear();
609 work->worklets.front()->output.ordinal = work->input.ordinal;
610 work->workletsProcessed = 1u;
611 return;
612 }
613
614 const C2ConstGraphicBlock inBuffer = inputBuffer->data().graphicBlocks().front();
615 if (inBuffer.width() < mSize->width || inBuffer.height() < mSize->height) {
616 ALOGE("unexpected Input buffer attributes %d(%d) x %d(%d)", inBuffer.width(), mSize->width,
617 inBuffer.height(), mSize->height);
618 mSignalledError = true;
619 work->result = C2_BAD_VALUE;
620 return;
621 }
622 bool end_of_stream = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
623 aom_image_t raw_frame;
624 const C2PlanarLayout& layout = rView->layout();
625 uint32_t width = mSize->width;
626 uint32_t height = mSize->height;
627 if (width > 0x8000 || height > 0x8000) {
628 ALOGE("Image too big: %u x %u", width, height);
629 work->result = C2_BAD_VALUE;
630 return;
631 }
632 uint32_t stride = (width + mStrideAlign - 1) & ~(mStrideAlign - 1);
633 uint32_t vstride = (height + mStrideAlign - 1) & ~(mStrideAlign - 1);
634 switch (layout.type) {
635 case C2PlanarLayout::TYPE_RGB:
636 case C2PlanarLayout::TYPE_RGBA: {
637 std::shared_ptr<C2StreamColorAspectsInfo::output> colorAspects;
638 {
639 IntfImpl::Lock lock = mIntf->lock();
640 colorAspects = mIntf->getCodedColorAspects_l();
641 }
642 ConvertRGBToPlanarYUV(mConversionBuffer.data(), stride, vstride,
643 mConversionBuffer.size(), *rView.get(), colorAspects->matrix,
644 colorAspects->range);
645 aom_img_wrap(&raw_frame, AOM_IMG_FMT_I420, width, height, mStrideAlign,
646 mConversionBuffer.data());
647 break;
648 }
649 case C2PlanarLayout::TYPE_YUV: {
650 if (!IsYUV420(*rView)) {
651 ALOGE("input is not YUV420");
652 work->result = C2_BAD_VALUE;
653 return;
654 }
655
656 if (layout.planes[layout.PLANE_Y].colInc == 1 &&
657 layout.planes[layout.PLANE_U].colInc == 1 &&
658 layout.planes[layout.PLANE_V].colInc == 1) {
659 // I420 compatible - though with custom offset and stride
660 aom_img_wrap(&raw_frame, AOM_IMG_FMT_I420, width, height, mStrideAlign,
661 (uint8_t*)rView->data()[0]);
662 raw_frame.planes[1] = (uint8_t*)rView->data()[1];
663 raw_frame.planes[2] = (uint8_t*)rView->data()[2];
664 raw_frame.stride[0] = layout.planes[layout.PLANE_Y].rowInc;
665 raw_frame.stride[1] = layout.planes[layout.PLANE_U].rowInc;
666 raw_frame.stride[2] = layout.planes[layout.PLANE_V].rowInc;
667 } else {
668 // copy to I420
669 MediaImage2 img = CreateYUV420PlanarMediaImage2(width, height, stride, vstride);
670 if (mConversionBuffer.size() >= stride * vstride * 3 / 2) {
671 status_t err = ImageCopy(mConversionBuffer.data(), &img, *rView);
672 if (err != OK) {
673 ALOGE("Buffer conversion failed: %d", err);
674 work->result = C2_BAD_VALUE;
675 return;
676 }
677 aom_img_wrap(&raw_frame, AOM_IMG_FMT_I420, stride, vstride, mStrideAlign,
678 mConversionBuffer.data());
679 aom_img_set_rect(&raw_frame, 0, 0, width, height, 0);
680 } else {
681 ALOGE("Conversion buffer is too small: %u x %u for %zu", stride, vstride,
682 mConversionBuffer.size());
683 work->result = C2_BAD_VALUE;
684 return;
685 }
686 }
687 break;
688 }
689 default:
690 ALOGE("Unrecognized plane type: %d", layout.type);
691 work->result = C2_BAD_VALUE;
692 return;
693 }
694
695 aom_enc_frame_flags_t flags = 0;
696 // handle dynamic config parameters
697 {
698 IntfImpl::Lock lock = mIntf->lock();
699 std::shared_ptr<C2StreamIntraRefreshTuning::output> intraRefresh =
700 mIntf->getIntraRefresh_l();
701 std::shared_ptr<C2StreamBitrateInfo::output> bitrate = mIntf->getBitrate_l();
702 std::shared_ptr<C2StreamRequestSyncFrameTuning::output> requestSync =
703 mIntf->getRequestSync_l();
704 lock.unlock();
705
706 if (intraRefresh != mIntraRefresh) {
707 mIntraRefresh = intraRefresh;
708 ALOGV("Got mIntraRefresh request");
709 }
710
711 if (requestSync != mRequestSync) {
712 // we can handle IDR immediately
713 if (requestSync->value) {
714 // unset request
715 C2StreamRequestSyncFrameTuning::output clearSync(0u, C2_FALSE);
716 std::vector<std::unique_ptr<C2SettingResult>> failures;
717 mIntf->config({&clearSync}, C2_MAY_BLOCK, &failures);
718 ALOGV("Got sync request");
719 flags |= AOM_EFLAG_FORCE_KF;
720 }
721 mRequestSync = requestSync;
722 }
723
724 if (bitrate != mBitrate) {
725 mBitrate = bitrate;
726 mCodecConfiguration->rc_target_bitrate = (mBitrate->value + 500) / 1000;
727 aom_codec_err_t res = aom_codec_enc_config_set(mCodecContext, mCodecConfiguration);
728 if (res != AOM_CODEC_OK) {
729 ALOGE("aom encoder failed to update bitrate: %s", aom_codec_err_to_string(res));
730 mSignalledError = true;
731 work->result = C2_CORRUPTED;
732 return;
733 }
734 }
735 }
736
737 uint64_t input_timestamp = work->input.ordinal.timestamp.peekull();
738 uint32_t frame_duration;
739 if (input_timestamp > mLastTimestamp) {
740 frame_duration = (uint32_t)(input_timestamp - mLastTimestamp);
741 } else {
742 // Use default of 30 fps in case of 0 frame rate.
743 float frame_rate = mFrameRate->value;
744 if (frame_rate < 0.001) {
745 frame_rate = 30.0;
746 }
747 frame_duration = (uint32_t)(1000000 / frame_rate + 0.5);
748 }
749 mLastTimestamp = input_timestamp;
750
751 aom_codec_err_t codec_return =
752 aom_codec_encode(mCodecContext, &raw_frame, input_timestamp, frame_duration, flags);
753 if (codec_return != AOM_CODEC_OK) {
754 ALOGE("aom encoder failed to encode frame");
755 mSignalledError = true;
756 work->result = C2_CORRUPTED;
757 return;
758 }
759
760 bool populated = false;
761 aom_codec_iter_t encoded_packet_iterator = nullptr;
762 const aom_codec_cx_pkt_t* encoded_packet;
763 while ((encoded_packet = aom_codec_get_cx_data(mCodecContext, &encoded_packet_iterator))) {
764 if (encoded_packet->kind == AOM_CODEC_CX_FRAME_PKT) {
765 std::shared_ptr<C2LinearBlock> block;
766 C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
767 c2_status_t err = pool->fetchLinearBlock(encoded_packet->data.frame.sz, usage, &block);
768 if (err != C2_OK) {
769 ALOGE("fetchLinearBlock for Output failed with status %d", err);
770 work->result = C2_NO_MEMORY;
771 return;
772 }
773 C2WriteView wView = block->map().get();
774 if (wView.error()) {
775 ALOGE("write view map failed %d", wView.error());
776 work->result = C2_CORRUPTED;
777 return;
778 }
779
780 memcpy(wView.data(), encoded_packet->data.frame.buf, encoded_packet->data.frame.sz);
781 ++mNumInputFrames;
782
783 ALOGD("bytes generated %zu", encoded_packet->data.frame.sz);
784 uint32_t flags = 0;
785 if (end_of_stream) {
786 flags |= C2FrameData::FLAG_END_OF_STREAM;
787 }
788
789 work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
790 work->worklets.front()->output.buffers.clear();
791 std::shared_ptr<C2Buffer> buffer =
792 createLinearBuffer(block, 0, encoded_packet->data.frame.sz);
793 if (encoded_packet->data.frame.flags & AOM_FRAME_IS_KEY) {
794 buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
795 0u /* stream id */, C2Config::SYNC_FRAME));
796 }
797 work->worklets.front()->output.buffers.push_back(buffer);
798 work->worklets.front()->output.ordinal = work->input.ordinal;
799 work->worklets.front()->output.ordinal.timestamp = encoded_packet->data.frame.pts;
800 work->workletsProcessed = 1u;
801 populated = true;
802 if (end_of_stream) {
803 mSignalledOutputEos = true;
804 ALOGV("signalled End Of Stream");
805 }
806 }
807 }
808 if (!populated) {
809 work->workletsProcessed = 0u;
810 }
811}
812
813c2_status_t C2SoftAomEnc::drain(uint32_t drainMode, const std::shared_ptr<C2BlockPool>& pool) {
814 (void)pool;
815 if (drainMode == NO_DRAIN) {
816 ALOGW("drain with NO_DRAIN: no-op");
817 return C2_OK;
818 }
819 if (drainMode == DRAIN_CHAIN) {
820 ALOGW("DRAIN_CHAIN not supported");
821 return C2_OMITTED;
822 }
823
824 return C2_OK;
825}
826
827class C2SoftAomEncFactory : public C2ComponentFactory {
828 public:
829 C2SoftAomEncFactory()
830 : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
831 GetCodec2PlatformComponentStore()->getParamReflector())) {}
832
833 virtual c2_status_t createComponent(c2_node_id_t id,
834 std::shared_ptr<C2Component>* const component,
835 std::function<void(C2Component*)> deleter) override {
836 *component = std::shared_ptr<C2Component>(
837 new C2SoftAomEnc(COMPONENT_NAME, id,
838 std::make_shared<C2SoftAomEnc::IntfImpl>(mHelper)),
839 deleter);
840 return C2_OK;
841 }
842
843 virtual c2_status_t createInterface(
844 c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
845 std::function<void(C2ComponentInterface*)> deleter) override {
846 *interface = std::shared_ptr<C2ComponentInterface>(
847 new SimpleInterface<C2SoftAomEnc::IntfImpl>(
848 COMPONENT_NAME, id, std::make_shared<C2SoftAomEnc::IntfImpl>(mHelper)),
849 deleter);
850 return C2_OK;
851 }
852
853 virtual ~C2SoftAomEncFactory() override = default;
854
855 private:
856 std::shared_ptr<C2ReflectorHelper> mHelper;
857};
858
859} // namespace android
860
861__attribute__((cfi_canonical_jump_table)) extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
862 ALOGV("in %s", __func__);
863 return new ::android::C2SoftAomEncFactory();
864}
865
866__attribute__((cfi_canonical_jump_table)) extern "C" void DestroyCodec2Factory(
867 ::C2ComponentFactory* factory) {
868 ALOGV("in %s", __func__);
869 delete factory;
870}