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