1 /*
2 * Copyright 2018 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 "C2SoftVpxEnc"
19 #include <log/log.h>
20 #include <utils/misc.h>
21
22 #include <media/hardware/VideoAPI.h>
23
24 #include <Codec2BufferUtils.h>
25 #include <C2Debug.h>
26 #include "C2SoftVpxEnc.h"
27
28 #ifndef INT32_MAX
29 #define INT32_MAX 2147483647
30 #endif
31
32 namespace android {
33
IntfImpl(const std::shared_ptr<C2ReflectorHelper> & helper)34 C2SoftVpxEnc::IntfImpl::IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
35 : SimpleInterface<void>::BaseParams(
36 helper,
37 COMPONENT_NAME,
38 C2Component::KIND_ENCODER,
39 C2Component::DOMAIN_VIDEO,
40 MEDIA_MIMETYPE_VIDEO) {
41 noPrivateBuffers(); // TODO: account for our buffers here
42 noInputReferences();
43 noOutputReferences();
44 noInputLatency();
45 noTimeStretch();
46 setDerivedInstance(this);
47
48 addParameter(
49 DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
50 .withConstValue(new C2ComponentAttributesSetting(
51 C2Component::ATTRIB_IS_TEMPORAL))
52 .build());
53
54 addParameter(
55 DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
56 .withConstValue(new C2StreamUsageTuning::input(
57 0u, (uint64_t)C2MemoryUsage::CPU_READ))
58 .build());
59
60 addParameter(
61 DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
62 .withDefault(new C2StreamPictureSizeInfo::input(0u, 64, 64))
63 .withFields({
64 C2F(mSize, width).inRange(2, 2048, 2),
65 C2F(mSize, height).inRange(2, 2048, 2),
66 })
67 .withSetter(SizeSetter)
68 .build());
69
70 addParameter(
71 DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE)
72 .withDefault(new C2StreamBitrateModeTuning::output(
73 0u, C2Config::BITRATE_VARIABLE))
74 .withFields({
75 C2F(mBitrateMode, value).oneOf({
76 C2Config::BITRATE_CONST, C2Config::BITRATE_VARIABLE })
77 })
78 .withSetter(
79 Setter<decltype(*mBitrateMode)>::StrictValueWithNoDeps)
80 .build());
81
82 addParameter(
83 DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
84 .withDefault(new C2StreamFrameRateInfo::output(0u, 1.))
85 // TODO: More restriction?
86 .withFields({C2F(mFrameRate, value).greaterThan(0.)})
87 .withSetter(
88 Setter<decltype(*mFrameRate)>::StrictValueWithNoDeps)
89 .build());
90
91 addParameter(
92 DefineParam(mLayering, C2_PARAMKEY_TEMPORAL_LAYERING)
93 .withDefault(C2StreamTemporalLayeringTuning::output::AllocShared(0u, 0, 0, 0))
94 .withFields({
95 C2F(mLayering, m.layerCount).inRange(0, 4),
96 C2F(mLayering, m.bLayerCount).inRange(0, 0),
97 C2F(mLayering, m.bitrateRatios).inRange(0., 1.)
98 })
99 .withSetter(LayeringSetter)
100 .build());
101
102 addParameter(
103 DefineParam(mSyncFramePeriod, C2_PARAMKEY_SYNC_FRAME_INTERVAL)
104 .withDefault(new C2StreamSyncFrameIntervalTuning::output(0u, 1000000))
105 .withFields({C2F(mSyncFramePeriod, value).any()})
106 .withSetter(Setter<decltype(*mSyncFramePeriod)>::StrictValueWithNoDeps)
107 .build());
108
109 addParameter(
110 DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
111 .withDefault(new C2StreamBitrateInfo::output(0u, 64000))
112 .withFields({C2F(mBitrate, value).inRange(4096, 40000000)})
113 .withSetter(BitrateSetter)
114 .build());
115
116 addParameter(
117 DefineParam(mIntraRefresh, C2_PARAMKEY_INTRA_REFRESH)
118 .withConstValue(new C2StreamIntraRefreshTuning::output(
119 0u, C2Config::INTRA_REFRESH_DISABLED, 0.))
120 .build());
121 #ifdef VP9
122 addParameter(
123 DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
124 .withDefault(new C2StreamProfileLevelInfo::output(
125 0u, PROFILE_VP9_0, LEVEL_VP9_4_1))
126 .withFields({
127 C2F(mProfileLevel, profile).equalTo(
128 PROFILE_VP9_0
129 ),
130 C2F(mProfileLevel, level).oneOf({
131 C2Config::LEVEL_VP9_1,
132 C2Config::LEVEL_VP9_1_1,
133 C2Config::LEVEL_VP9_2,
134 C2Config::LEVEL_VP9_2_1,
135 C2Config::LEVEL_VP9_3,
136 C2Config::LEVEL_VP9_3_1,
137 C2Config::LEVEL_VP9_4,
138 C2Config::LEVEL_VP9_4_1,
139 }),
140 })
141 .withSetter(ProfileLevelSetter, mSize, mFrameRate, mBitrate)
142 .build());
143 #else
144 addParameter(
145 DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
146 .withDefault(new C2StreamProfileLevelInfo::output(
147 0u, PROFILE_VP8_0, LEVEL_UNUSED))
148 .withFields({
149 C2F(mProfileLevel, profile).equalTo(
150 PROFILE_VP8_0
151 ),
152 C2F(mProfileLevel, level).equalTo(
153 LEVEL_UNUSED),
154 })
155 .withSetter(ProfileLevelSetter, mSize, mFrameRate, mBitrate)
156 .build());
157 #endif
158 addParameter(
159 DefineParam(mRequestSync, C2_PARAMKEY_REQUEST_SYNC_FRAME)
160 .withDefault(new C2StreamRequestSyncFrameTuning::output(0u, C2_FALSE))
161 .withFields({C2F(mRequestSync, value).oneOf({ C2_FALSE, C2_TRUE }) })
162 .withSetter(Setter<decltype(*mRequestSync)>::NonStrictValueWithNoDeps)
163 .build());
164
165 addParameter(
166 DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
167 .withDefault(new C2StreamColorAspectsInfo::input(
168 0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
169 C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
170 .withFields({
171 C2F(mColorAspects, range).inRange(
172 C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
173 C2F(mColorAspects, primaries).inRange(
174 C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
175 C2F(mColorAspects, transfer).inRange(
176 C2Color::TRANSFER_UNSPECIFIED, C2Color::TRANSFER_OTHER),
177 C2F(mColorAspects, matrix).inRange(
178 C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)
179 })
180 .withSetter(ColorAspectsSetter)
181 .build());
182
183 addParameter(
184 DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
185 .withDefault(new C2StreamColorAspectsInfo::output(
186 0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
187 C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
188 .withFields({
189 C2F(mCodedColorAspects, range).inRange(
190 C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
191 C2F(mCodedColorAspects, primaries).inRange(
192 C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
193 C2F(mCodedColorAspects, transfer).inRange(
194 C2Color::TRANSFER_UNSPECIFIED, C2Color::TRANSFER_OTHER),
195 C2F(mCodedColorAspects, matrix).inRange(
196 C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)
197 })
198 .withSetter(CodedColorAspectsSetter, mColorAspects)
199 .build());
200 }
201
BitrateSetter(bool mayBlock,C2P<C2StreamBitrateInfo::output> & me)202 C2R C2SoftVpxEnc::IntfImpl::BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output> &me) {
203 (void)mayBlock;
204 C2R res = C2R::Ok();
205 if (me.v.value < 4096) {
206 me.set().value = 4096;
207 }
208 return res;
209 }
210
SizeSetter(bool mayBlock,const C2P<C2StreamPictureSizeInfo::input> & oldMe,C2P<C2StreamPictureSizeInfo::input> & me)211 C2R C2SoftVpxEnc::IntfImpl::SizeSetter(bool mayBlock,
212 const C2P<C2StreamPictureSizeInfo::input>& oldMe,
213 C2P<C2StreamPictureSizeInfo::input>& me) {
214 (void)mayBlock;
215 C2R res = C2R::Ok();
216 if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
217 res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
218 me.set().width = oldMe.v.width;
219 }
220 if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
221 res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
222 me.set().height = oldMe.v.height;
223 }
224 return res;
225 }
226
ProfileLevelSetter(bool mayBlock,C2P<C2StreamProfileLevelInfo::output> & me,const C2P<C2StreamPictureSizeInfo::input> & size,const C2P<C2StreamFrameRateInfo::output> & frameRate,const C2P<C2StreamBitrateInfo::output> & bitrate)227 C2R C2SoftVpxEnc::IntfImpl::ProfileLevelSetter(bool mayBlock,
228 C2P<C2StreamProfileLevelInfo::output>& me,
229 const C2P<C2StreamPictureSizeInfo::input>& size,
230 const C2P<C2StreamFrameRateInfo::output>& frameRate,
231 const C2P<C2StreamBitrateInfo::output>& bitrate) {
232 (void)mayBlock;
233 #ifdef VP9
234 if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
235 me.set().profile = PROFILE_VP9_0;
236 }
237 struct LevelLimits {
238 C2Config::level_t level;
239 float samplesPerSec;
240 uint64_t samples;
241 uint32_t bitrate;
242 size_t dimension;
243 };
244 constexpr LevelLimits kLimits[] = {
245 {LEVEL_VP9_1, 829440, 36864, 200000, 512},
246 {LEVEL_VP9_1_1, 2764800, 73728, 800000, 768},
247 {LEVEL_VP9_2, 4608000, 122880, 1800000, 960},
248 {LEVEL_VP9_2_1, 9216000, 245760, 3600000, 1344},
249 {LEVEL_VP9_3, 20736000, 552960, 7200000, 2048},
250 {LEVEL_VP9_3_1, 36864000, 983040, 12000000, 2752},
251 {LEVEL_VP9_4, 83558400, 2228224, 18000000, 4160},
252 {LEVEL_VP9_4_1, 160432128, 2228224, 30000000, 4160},
253 };
254
255 uint64_t samples = size.v.width * size.v.height;
256 float samplesPerSec = float(samples) * frameRate.v.value;
257 size_t dimension = std::max(size.v.width, size.v.height);
258
259 // Check if the supplied level meets the samples / bitrate requirements.
260 // If not, update the level with the lowest level meeting the requirements.
261 bool found = false;
262
263 // By default needsUpdate = false in case the supplied level does meet
264 // the requirements.
265 bool needsUpdate = false;
266 if (!me.F(me.v.level).supportsAtAll(me.v.level)) {
267 needsUpdate = true;
268 }
269 for (const LevelLimits& limit : kLimits) {
270 if (samples <= limit.samples && samplesPerSec <= limit.samplesPerSec &&
271 bitrate.v.value <= limit.bitrate && dimension <= limit.dimension) {
272 // This is the lowest level that meets the requirements, and if
273 // we haven't seen the supplied level yet, that means we don't
274 // need the update.
275 if (needsUpdate) {
276 ALOGD("Given level %x does not cover current configuration: "
277 "adjusting to %x",
278 me.v.level, limit.level);
279 me.set().level = limit.level;
280 }
281 found = true;
282 break;
283 }
284 if (me.v.level == limit.level) {
285 // We break out of the loop when the lowest feasible level is
286 // found. The fact that we're here means that our level doesn't
287 // meet the requirement and needs to be updated.
288 needsUpdate = true;
289 }
290 }
291 if (!found) {
292 // We set to the highest supported level.
293 me.set().level = LEVEL_VP9_4_1;
294 }
295 #else
296 (void)size;
297 (void)frameRate;
298 (void)bitrate;
299 if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
300 me.set().profile = PROFILE_VP8_0;
301 }
302 if (!me.F(me.v.level).supportsAtAll(me.v.level)) {
303 me.set().level = LEVEL_UNUSED;
304 }
305 #endif
306 return C2R::Ok();
307 }
308
LayeringSetter(bool mayBlock,C2P<C2StreamTemporalLayeringTuning::output> & me)309 C2R C2SoftVpxEnc::IntfImpl::LayeringSetter(bool mayBlock,
310 C2P<C2StreamTemporalLayeringTuning::output>& me) {
311 (void)mayBlock;
312 C2R res = C2R::Ok();
313 if (me.v.m.layerCount > 4) {
314 me.set().m.layerCount = 4;
315 }
316 me.set().m.bLayerCount = 0;
317 // ensure ratios are monotonic and clamped between 0 and 1
318 for (size_t ix = 0; ix < me.v.flexCount(); ++ix) {
319 me.set().m.bitrateRatios[ix] = c2_clamp(
320 ix > 0 ? me.v.m.bitrateRatios[ix - 1] : 0, me.v.m.bitrateRatios[ix], 1.);
321 }
322 ALOGI("setting temporal layering %u + %u", me.v.m.layerCount, me.v.m.bLayerCount);
323 return res;
324 }
325
getSyncFramePeriod() const326 uint32_t C2SoftVpxEnc::IntfImpl::getSyncFramePeriod() const {
327 if (mSyncFramePeriod->value < 0 || mSyncFramePeriod->value == INT64_MAX) {
328 return 0;
329 }
330 double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value;
331 return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.);
332 }
ColorAspectsSetter(bool mayBlock,C2P<C2StreamColorAspectsInfo::input> & me)333 C2R C2SoftVpxEnc::IntfImpl::ColorAspectsSetter(bool mayBlock,
334 C2P<C2StreamColorAspectsInfo::input>& me) {
335 (void)mayBlock;
336 if (me.v.range > C2Color::RANGE_OTHER) {
337 me.set().range = C2Color::RANGE_OTHER;
338 }
339 if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
340 me.set().primaries = C2Color::PRIMARIES_OTHER;
341 }
342 if (me.v.transfer > C2Color::TRANSFER_OTHER) {
343 me.set().transfer = C2Color::TRANSFER_OTHER;
344 }
345 if (me.v.matrix > C2Color::MATRIX_OTHER) {
346 me.set().matrix = C2Color::MATRIX_OTHER;
347 }
348 return C2R::Ok();
349 }
CodedColorAspectsSetter(bool mayBlock,C2P<C2StreamColorAspectsInfo::output> & me,const C2P<C2StreamColorAspectsInfo::input> & coded)350 C2R C2SoftVpxEnc::IntfImpl::CodedColorAspectsSetter(
351 bool mayBlock, C2P<C2StreamColorAspectsInfo::output>& me,
352 const C2P<C2StreamColorAspectsInfo::input>& coded) {
353 (void)mayBlock;
354 me.set().range = coded.v.range;
355 me.set().primaries = coded.v.primaries;
356 me.set().transfer = coded.v.transfer;
357 me.set().matrix = coded.v.matrix;
358 return C2R::Ok();
359 }
360
361 #if 0
362 static size_t getCpuCoreCount() {
363 long cpuCoreCount = 1;
364 #if defined(_SC_NPROCESSORS_ONLN)
365 cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
366 #else
367 // _SC_NPROC_ONLN must be defined...
368 cpuCoreCount = sysconf(_SC_NPROC_ONLN);
369 #endif
370 CHECK(cpuCoreCount >= 1);
371 ALOGV("Number of CPU cores: %ld", cpuCoreCount);
372 return (size_t)cpuCoreCount;
373 }
374 #endif
375
C2SoftVpxEnc(const char * name,c2_node_id_t id,const std::shared_ptr<IntfImpl> & intfImpl)376 C2SoftVpxEnc::C2SoftVpxEnc(const char* name, c2_node_id_t id,
377 const std::shared_ptr<IntfImpl>& intfImpl)
378 : SimpleC2Component(
379 std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
380 mIntf(intfImpl),
381 mCodecContext(nullptr),
382 mCodecConfiguration(nullptr),
383 mCodecInterface(nullptr),
384 mStrideAlign(2),
385 mColorFormat(VPX_IMG_FMT_I420),
386 mBitrateControlMode(VPX_VBR),
387 mErrorResilience(false),
388 mMinQuantizer(0),
389 mMaxQuantizer(0),
390 mTemporalLayers(0),
391 mTemporalPatternType(VPXTemporalLayerPatternNone),
392 mTemporalPatternLength(0),
393 mTemporalPatternIdx(0),
394 mLastTimestamp(0x7FFFFFFFFFFFFFFFull),
395 mSignalledOutputEos(false),
396 mSignalledError(false) {
397 for (int i = 0; i < MAXTEMPORALLAYERS; i++) {
398 mTemporalLayerBitrateRatio[i] = 1.0f;
399 }
400 }
401
~C2SoftVpxEnc()402 C2SoftVpxEnc::~C2SoftVpxEnc() {
403 onRelease();
404 }
405
onInit()406 c2_status_t C2SoftVpxEnc::onInit() {
407 status_t err = initEncoder();
408 return err == OK ? C2_OK : C2_CORRUPTED;
409 }
410
onRelease()411 void C2SoftVpxEnc::onRelease() {
412 if (mCodecContext) {
413 vpx_codec_destroy(mCodecContext);
414 delete mCodecContext;
415 mCodecContext = nullptr;
416 }
417
418 if (mCodecConfiguration) {
419 delete mCodecConfiguration;
420 mCodecConfiguration = nullptr;
421 }
422
423 // this one is not allocated by us
424 mCodecInterface = nullptr;
425 }
426
onStop()427 c2_status_t C2SoftVpxEnc::onStop() {
428 onRelease();
429 mLastTimestamp = 0x7FFFFFFFFFFFFFFFLL;
430 mSignalledOutputEos = false;
431 mSignalledError = false;
432 return C2_OK;
433 }
434
onReset()435 void C2SoftVpxEnc::onReset() {
436 (void)onStop();
437 }
438
onFlush_sm()439 c2_status_t C2SoftVpxEnc::onFlush_sm() {
440 return onStop();
441 }
442
initEncoder()443 status_t C2SoftVpxEnc::initEncoder() {
444 vpx_codec_err_t codec_return;
445 status_t result = UNKNOWN_ERROR;
446 {
447 IntfImpl::Lock lock = mIntf->lock();
448 mSize = mIntf->getSize_l();
449 mBitrate = mIntf->getBitrate_l();
450 mBitrateMode = mIntf->getBitrateMode_l();
451 mFrameRate = mIntf->getFrameRate_l();
452 mIntraRefresh = mIntf->getIntraRefresh_l();
453 mRequestSync = mIntf->getRequestSync_l();
454 mLayering = mIntf->getTemporalLayers_l();
455 mTemporalLayers = mLayering->m.layerCount;
456 }
457
458 switch (mBitrateMode->value) {
459 case C2Config::BITRATE_CONST:
460 mBitrateControlMode = VPX_CBR;
461 break;
462 case C2Config::BITRATE_VARIABLE:
463 [[fallthrough]];
464 default:
465 mBitrateControlMode = VPX_VBR;
466 break;
467 }
468
469 setCodecSpecificInterface();
470 if (!mCodecInterface) goto CleanUp;
471
472 ALOGD("VPx: initEncoder. BRMode: %u. TSLayers: %zu. KF: %u. QP: %u - %u",
473 (uint32_t)mBitrateControlMode, mTemporalLayers, mIntf->getSyncFramePeriod(),
474 mMinQuantizer, mMaxQuantizer);
475
476 mCodecConfiguration = new vpx_codec_enc_cfg_t;
477 if (!mCodecConfiguration) goto CleanUp;
478 codec_return = vpx_codec_enc_config_default(mCodecInterface,
479 mCodecConfiguration,
480 0);
481 if (codec_return != VPX_CODEC_OK) {
482 ALOGE("Error populating default configuration for vpx encoder.");
483 goto CleanUp;
484 }
485
486 mCodecConfiguration->g_w = mSize->width;
487 mCodecConfiguration->g_h = mSize->height;
488 //mCodecConfiguration->g_threads = getCpuCoreCount();
489 mCodecConfiguration->g_threads = 0;
490 mCodecConfiguration->g_error_resilient = mErrorResilience;
491
492 // timebase unit is microsecond
493 // g_timebase is in seconds (i.e. 1/1000000 seconds)
494 mCodecConfiguration->g_timebase.num = 1;
495 mCodecConfiguration->g_timebase.den = 1000000;
496 // rc_target_bitrate is in kbps, mBitrate in bps
497 mCodecConfiguration->rc_target_bitrate = (mBitrate->value + 500) / 1000;
498 mCodecConfiguration->rc_end_usage = mBitrateControlMode;
499 // Disable frame drop - not allowed in MediaCodec now.
500 mCodecConfiguration->rc_dropframe_thresh = 0;
501 // Disable lagged encoding.
502 mCodecConfiguration->g_lag_in_frames = 0;
503 if (mBitrateControlMode == VPX_CBR) {
504 // Disable spatial resizing.
505 mCodecConfiguration->rc_resize_allowed = 0;
506 // Single-pass mode.
507 mCodecConfiguration->g_pass = VPX_RC_ONE_PASS;
508 // Maximum amount of bits that can be subtracted from the target
509 // bitrate - expressed as percentage of the target bitrate.
510 mCodecConfiguration->rc_undershoot_pct = 100;
511 // Maximum amount of bits that can be added to the target
512 // bitrate - expressed as percentage of the target bitrate.
513 mCodecConfiguration->rc_overshoot_pct = 15;
514 // Initial value of the buffer level in ms.
515 mCodecConfiguration->rc_buf_initial_sz = 500;
516 // Amount of data that the encoder should try to maintain in ms.
517 mCodecConfiguration->rc_buf_optimal_sz = 600;
518 // The amount of data that may be buffered by the decoding
519 // application in ms.
520 mCodecConfiguration->rc_buf_sz = 1000;
521 // Enable error resilience - needed for packet loss.
522 mCodecConfiguration->g_error_resilient = 1;
523 // Maximum key frame interval - for CBR boost to 3000
524 mCodecConfiguration->kf_max_dist = 3000;
525 // Encoder determines optimal key frame placement automatically.
526 mCodecConfiguration->kf_mode = VPX_KF_AUTO;
527 }
528
529 // Frames temporal pattern - for now WebRTC like pattern is only supported.
530 switch (mTemporalLayers) {
531 case 0:
532 mTemporalPatternLength = 0;
533 break;
534 case 1:
535 mCodecConfiguration->ts_number_layers = 1;
536 mCodecConfiguration->ts_rate_decimator[0] = 1;
537 mCodecConfiguration->ts_periodicity = 1;
538 mCodecConfiguration->ts_layer_id[0] = 0;
539 mTemporalPattern[0] = kTemporalUpdateLastRefAll;
540 mTemporalPatternLength = 1;
541 break;
542 case 2:
543 mCodecConfiguration->ts_number_layers = 2;
544 mCodecConfiguration->ts_rate_decimator[0] = 2;
545 mCodecConfiguration->ts_rate_decimator[1] = 1;
546 mCodecConfiguration->ts_periodicity = 2;
547 mCodecConfiguration->ts_layer_id[0] = 0;
548 mCodecConfiguration->ts_layer_id[1] = 1;
549 mTemporalPattern[0] = kTemporalUpdateLastAndGoldenRefAltRef;
550 mTemporalPattern[1] = kTemporalUpdateGoldenWithoutDependencyRefAltRef;
551 mTemporalPattern[2] = kTemporalUpdateLastRefAltRef;
552 mTemporalPattern[3] = kTemporalUpdateGoldenRefAltRef;
553 mTemporalPattern[4] = kTemporalUpdateLastRefAltRef;
554 mTemporalPattern[5] = kTemporalUpdateGoldenRefAltRef;
555 mTemporalPattern[6] = kTemporalUpdateLastRefAltRef;
556 mTemporalPattern[7] = kTemporalUpdateNone;
557 mTemporalLayerBitrateRatio[0] = mLayering->m.bitrateRatios[0];
558 mTemporalPatternLength = 8;
559 break;
560 case 3:
561 mCodecConfiguration->ts_number_layers = 3;
562 mCodecConfiguration->ts_rate_decimator[0] = 4;
563 mCodecConfiguration->ts_rate_decimator[1] = 2;
564 mCodecConfiguration->ts_rate_decimator[2] = 1;
565 mCodecConfiguration->ts_periodicity = 4;
566 mCodecConfiguration->ts_layer_id[0] = 0;
567 mCodecConfiguration->ts_layer_id[1] = 2;
568 mCodecConfiguration->ts_layer_id[2] = 1;
569 mCodecConfiguration->ts_layer_id[3] = 2;
570 mTemporalPattern[0] = kTemporalUpdateLastAndGoldenRefAltRef;
571 mTemporalPattern[1] = kTemporalUpdateNoneNoRefGoldenRefAltRef;
572 mTemporalPattern[2] = kTemporalUpdateGoldenWithoutDependencyRefAltRef;
573 mTemporalPattern[3] = kTemporalUpdateNone;
574 mTemporalPattern[4] = kTemporalUpdateLastRefAltRef;
575 mTemporalPattern[5] = kTemporalUpdateNone;
576 mTemporalPattern[6] = kTemporalUpdateGoldenRefAltRef;
577 mTemporalPattern[7] = kTemporalUpdateNone;
578 mTemporalLayerBitrateRatio[0] = mLayering->m.bitrateRatios[0];
579 mTemporalLayerBitrateRatio[1] = mLayering->m.bitrateRatios[1];
580 mTemporalPatternLength = 8;
581 break;
582 default:
583 ALOGE("Wrong number of temporal layers %zu", mTemporalLayers);
584 goto CleanUp;
585 }
586 // Set bitrate values for each layer
587 for (size_t i = 0; i < mCodecConfiguration->ts_number_layers; i++) {
588 mCodecConfiguration->ts_target_bitrate[i] =
589 mCodecConfiguration->rc_target_bitrate *
590 mTemporalLayerBitrateRatio[i];
591 }
592 if (mIntf->getSyncFramePeriod() >= 0) {
593 mCodecConfiguration->kf_max_dist = mIntf->getSyncFramePeriod();
594 mCodecConfiguration->kf_min_dist = mIntf->getSyncFramePeriod();
595 mCodecConfiguration->kf_mode = VPX_KF_AUTO;
596 }
597 if (mMinQuantizer > 0) {
598 mCodecConfiguration->rc_min_quantizer = mMinQuantizer;
599 }
600 if (mMaxQuantizer > 0) {
601 mCodecConfiguration->rc_max_quantizer = mMaxQuantizer;
602 }
603 setCodecSpecificConfiguration();
604 mCodecContext = new vpx_codec_ctx_t;
605 if (!mCodecContext) goto CleanUp;
606 codec_return = vpx_codec_enc_init(mCodecContext,
607 mCodecInterface,
608 mCodecConfiguration,
609 0); // flags
610 if (codec_return != VPX_CODEC_OK) {
611 ALOGE("Error initializing vpx encoder");
612 goto CleanUp;
613 }
614
615 // Extra CBR settings
616 if (mBitrateControlMode == VPX_CBR) {
617 codec_return = vpx_codec_control(mCodecContext,
618 VP8E_SET_STATIC_THRESHOLD,
619 1);
620 if (codec_return == VPX_CODEC_OK) {
621 uint32_t rc_max_intra_target =
622 (uint32_t)(mCodecConfiguration->rc_buf_optimal_sz * mFrameRate->value / 20 + 0.5);
623 // Don't go below 3 times per frame bandwidth.
624 if (rc_max_intra_target < 300) {
625 rc_max_intra_target = 300;
626 }
627 codec_return = vpx_codec_control(mCodecContext,
628 VP8E_SET_MAX_INTRA_BITRATE_PCT,
629 rc_max_intra_target);
630 }
631 if (codec_return == VPX_CODEC_OK) {
632 codec_return = vpx_codec_control(mCodecContext,
633 VP8E_SET_CPUUSED,
634 -8);
635 }
636 if (codec_return != VPX_CODEC_OK) {
637 ALOGE("Error setting cbr parameters for vpx encoder.");
638 goto CleanUp;
639 }
640 }
641
642 codec_return = setCodecSpecificControls();
643 if (codec_return != VPX_CODEC_OK) goto CleanUp;
644
645 {
646 uint32_t width = mSize->width;
647 uint32_t height = mSize->height;
648 if (((uint64_t)width * height) >
649 ((uint64_t)INT32_MAX / 3)) {
650 ALOGE("b/25812794, Buffer size is too big, width=%u, height=%u.", width, height);
651 } else {
652 uint32_t stride = (width + mStrideAlign - 1) & ~(mStrideAlign - 1);
653 uint32_t vstride = (height + mStrideAlign - 1) & ~(mStrideAlign - 1);
654 mConversionBuffer = MemoryBlock::Allocate(stride * vstride * 3 / 2);
655 if (!mConversionBuffer.size()) {
656 ALOGE("Allocating conversion buffer failed.");
657 } else {
658 mNumInputFrames = -1;
659 return OK;
660 }
661 }
662 }
663
664 CleanUp:
665 onRelease();
666 return result;
667 }
668
getEncodeFlags()669 vpx_enc_frame_flags_t C2SoftVpxEnc::getEncodeFlags() {
670 vpx_enc_frame_flags_t flags = 0;
671 if (mTemporalPatternLength > 0) {
672 int patternIdx = mTemporalPatternIdx % mTemporalPatternLength;
673 mTemporalPatternIdx++;
674 switch (mTemporalPattern[patternIdx]) {
675 case kTemporalUpdateLast:
676 flags |= VP8_EFLAG_NO_UPD_GF;
677 flags |= VP8_EFLAG_NO_UPD_ARF;
678 flags |= VP8_EFLAG_NO_REF_GF;
679 flags |= VP8_EFLAG_NO_REF_ARF;
680 break;
681 case kTemporalUpdateGoldenWithoutDependency:
682 flags |= VP8_EFLAG_NO_REF_GF;
683 [[fallthrough]];
684 case kTemporalUpdateGolden:
685 flags |= VP8_EFLAG_NO_REF_ARF;
686 flags |= VP8_EFLAG_NO_UPD_ARF;
687 flags |= VP8_EFLAG_NO_UPD_LAST;
688 break;
689 case kTemporalUpdateAltrefWithoutDependency:
690 flags |= VP8_EFLAG_NO_REF_ARF;
691 flags |= VP8_EFLAG_NO_REF_GF;
692 [[fallthrough]];
693 case kTemporalUpdateAltref:
694 flags |= VP8_EFLAG_NO_UPD_GF;
695 flags |= VP8_EFLAG_NO_UPD_LAST;
696 break;
697 case kTemporalUpdateNoneNoRefAltref:
698 flags |= VP8_EFLAG_NO_REF_ARF;
699 [[fallthrough]];
700 case kTemporalUpdateNone:
701 flags |= VP8_EFLAG_NO_UPD_GF;
702 flags |= VP8_EFLAG_NO_UPD_ARF;
703 flags |= VP8_EFLAG_NO_UPD_LAST;
704 flags |= VP8_EFLAG_NO_UPD_ENTROPY;
705 break;
706 case kTemporalUpdateNoneNoRefGoldenRefAltRef:
707 flags |= VP8_EFLAG_NO_REF_GF;
708 flags |= VP8_EFLAG_NO_UPD_GF;
709 flags |= VP8_EFLAG_NO_UPD_ARF;
710 flags |= VP8_EFLAG_NO_UPD_LAST;
711 flags |= VP8_EFLAG_NO_UPD_ENTROPY;
712 break;
713 case kTemporalUpdateGoldenWithoutDependencyRefAltRef:
714 flags |= VP8_EFLAG_NO_REF_GF;
715 flags |= VP8_EFLAG_NO_UPD_ARF;
716 flags |= VP8_EFLAG_NO_UPD_LAST;
717 break;
718 case kTemporalUpdateLastRefAltRef:
719 flags |= VP8_EFLAG_NO_UPD_GF;
720 flags |= VP8_EFLAG_NO_UPD_ARF;
721 flags |= VP8_EFLAG_NO_REF_GF;
722 break;
723 case kTemporalUpdateGoldenRefAltRef:
724 flags |= VP8_EFLAG_NO_UPD_ARF;
725 flags |= VP8_EFLAG_NO_UPD_LAST;
726 break;
727 case kTemporalUpdateLastAndGoldenRefAltRef:
728 flags |= VP8_EFLAG_NO_UPD_ARF;
729 flags |= VP8_EFLAG_NO_REF_GF;
730 break;
731 case kTemporalUpdateLastRefAll:
732 flags |= VP8_EFLAG_NO_UPD_ARF;
733 flags |= VP8_EFLAG_NO_UPD_GF;
734 break;
735 }
736 }
737 return flags;
738 }
739
740 // TODO: add support for YUV input color formats
741 // TODO: add support for SVC, ARF. SVC and ARF returns multiple frames
742 // (hierarchical / noshow) in one call. These frames should be combined in to
743 // a single buffer and sent back to the client
process(const std::unique_ptr<C2Work> & work,const std::shared_ptr<C2BlockPool> & pool)744 void C2SoftVpxEnc::process(
745 const std::unique_ptr<C2Work> &work,
746 const std::shared_ptr<C2BlockPool> &pool) {
747 // Initialize output work
748 work->result = C2_OK;
749 work->workletsProcessed = 1u;
750 work->worklets.front()->output.flags = work->input.flags;
751
752 if (mSignalledError || mSignalledOutputEos) {
753 work->result = C2_BAD_VALUE;
754 return;
755 }
756 // Initialize encoder if not already
757 if (!mCodecContext && OK != initEncoder()) {
758 ALOGE("Failed to initialize encoder");
759 mSignalledError = true;
760 work->result = C2_CORRUPTED;
761 return;
762 }
763
764 std::shared_ptr<C2GraphicView> rView;
765 std::shared_ptr<C2Buffer> inputBuffer;
766 if (!work->input.buffers.empty()) {
767 inputBuffer = work->input.buffers[0];
768 rView = std::make_shared<C2GraphicView>(
769 inputBuffer->data().graphicBlocks().front().map().get());
770 if (rView->error() != C2_OK) {
771 ALOGE("graphic view map err = %d", rView->error());
772 work->result = C2_CORRUPTED;
773 return;
774 }
775 //(b/232396154)
776 //workaround for incorrect crop size in view when using surface mode
777 rView->setCrop_be(C2Rect(mSize->width, mSize->height));
778 } else {
779 ALOGV("Empty input Buffer");
780 uint32_t flags = 0;
781 if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
782 flags |= C2FrameData::FLAG_END_OF_STREAM;
783 }
784 work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
785 work->worklets.front()->output.buffers.clear();
786 work->worklets.front()->output.ordinal = work->input.ordinal;
787 work->workletsProcessed = 1u;
788 return;
789 }
790
791 const C2ConstGraphicBlock inBuffer =
792 inputBuffer->data().graphicBlocks().front();
793 if (inBuffer.width() < mSize->width ||
794 inBuffer.height() < mSize->height) {
795 ALOGE("unexpected Input buffer attributes %d(%d) x %d(%d)",
796 inBuffer.width(), mSize->width, inBuffer.height(),
797 mSize->height);
798 mSignalledError = true;
799 work->result = C2_BAD_VALUE;
800 return;
801 }
802 bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
803 vpx_image_t raw_frame;
804 const C2PlanarLayout &layout = rView->layout();
805 uint32_t width = mSize->width;
806 uint32_t height = mSize->height;
807 if (width > 0x8000 || height > 0x8000) {
808 ALOGE("Image too big: %u x %u", width, height);
809 work->result = C2_BAD_VALUE;
810 return;
811 }
812 uint32_t stride = (width + mStrideAlign - 1) & ~(mStrideAlign - 1);
813 uint32_t vstride = (height + mStrideAlign - 1) & ~(mStrideAlign - 1);
814 switch (layout.type) {
815 case C2PlanarLayout::TYPE_RGB:
816 case C2PlanarLayout::TYPE_RGBA: {
817 std::shared_ptr<C2StreamColorAspectsInfo::output> colorAspects;
818 {
819 IntfImpl::Lock lock = mIntf->lock();
820 colorAspects = mIntf->getCodedColorAspects_l();
821 }
822 ConvertRGBToPlanarYUV(mConversionBuffer.data(), stride, vstride,
823 mConversionBuffer.size(), *rView.get(),
824 colorAspects->matrix, colorAspects->range);
825 vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, width, height,
826 mStrideAlign, mConversionBuffer.data());
827 break;
828 }
829 case C2PlanarLayout::TYPE_YUV: {
830 if (!IsYUV420(*rView)) {
831 ALOGE("input is not YUV420");
832 work->result = C2_BAD_VALUE;
833 return;
834 }
835
836 if (layout.planes[layout.PLANE_Y].colInc == 1
837 && layout.planes[layout.PLANE_U].colInc == 1
838 && layout.planes[layout.PLANE_V].colInc == 1) {
839 // I420 compatible - though with custom offset and stride
840 vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, width, height,
841 mStrideAlign, (uint8_t*)rView->data()[0]);
842 raw_frame.planes[1] = (uint8_t*)rView->data()[1];
843 raw_frame.planes[2] = (uint8_t*)rView->data()[2];
844 raw_frame.stride[0] = layout.planes[layout.PLANE_Y].rowInc;
845 raw_frame.stride[1] = layout.planes[layout.PLANE_U].rowInc;
846 raw_frame.stride[2] = layout.planes[layout.PLANE_V].rowInc;
847 } else {
848 // copy to I420
849 MediaImage2 img = CreateYUV420PlanarMediaImage2(width, height, stride, vstride);
850 if (mConversionBuffer.size() >= stride * vstride * 3 / 2) {
851 status_t err = ImageCopy(mConversionBuffer.data(), &img, *rView);
852 if (err != OK) {
853 ALOGE("Buffer conversion failed: %d", err);
854 work->result = C2_BAD_VALUE;
855 return;
856 }
857 vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, stride, vstride,
858 mStrideAlign, mConversionBuffer.data());
859 vpx_img_set_rect(&raw_frame, 0, 0, width, height);
860 } else {
861 ALOGE("Conversion buffer is too small: %u x %u for %zu",
862 stride, vstride, mConversionBuffer.size());
863 work->result = C2_BAD_VALUE;
864 return;
865 }
866 }
867 break;
868 }
869 default:
870 ALOGE("Unrecognized plane type: %d", layout.type);
871 work->result = C2_BAD_VALUE;
872 return;
873 }
874
875 vpx_enc_frame_flags_t flags = getEncodeFlags();
876 // handle dynamic config parameters
877 {
878 IntfImpl::Lock lock = mIntf->lock();
879 std::shared_ptr<C2StreamIntraRefreshTuning::output> intraRefresh = mIntf->getIntraRefresh_l();
880 std::shared_ptr<C2StreamBitrateInfo::output> bitrate = mIntf->getBitrate_l();
881 std::shared_ptr<C2StreamRequestSyncFrameTuning::output> requestSync = mIntf->getRequestSync_l();
882 lock.unlock();
883
884 if (intraRefresh != mIntraRefresh) {
885 mIntraRefresh = intraRefresh;
886 ALOGV("Got mIntraRefresh request");
887 }
888
889 if (requestSync != mRequestSync) {
890 // we can handle IDR immediately
891 if (requestSync->value) {
892 // unset request
893 C2StreamRequestSyncFrameTuning::output clearSync(0u, C2_FALSE);
894 std::vector<std::unique_ptr<C2SettingResult>> failures;
895 mIntf->config({ &clearSync }, C2_MAY_BLOCK, &failures);
896 ALOGV("Got sync request");
897 flags |= VPX_EFLAG_FORCE_KF;
898 }
899 mRequestSync = requestSync;
900 }
901
902 if (bitrate != mBitrate) {
903 mBitrate = bitrate;
904 mCodecConfiguration->rc_target_bitrate =
905 (mBitrate->value + 500) / 1000;
906 vpx_codec_err_t res = vpx_codec_enc_config_set(mCodecContext,
907 mCodecConfiguration);
908 if (res != VPX_CODEC_OK) {
909 ALOGE("vpx encoder failed to update bitrate: %s",
910 vpx_codec_err_to_string(res));
911 mSignalledError = true;
912 work->result = C2_CORRUPTED;
913 return;
914 }
915 }
916 }
917
918 uint64_t inputTimeStamp = work->input.ordinal.timestamp.peekull();
919 uint32_t frameDuration;
920 if (inputTimeStamp > mLastTimestamp) {
921 frameDuration = (uint32_t)(inputTimeStamp - mLastTimestamp);
922 } else {
923 // Use default of 30 fps in case of 0 frame rate.
924 float frameRate = mFrameRate->value;
925 if (frameRate < 0.001) {
926 frameRate = 30;
927 }
928 frameDuration = (uint32_t)(1000000 / frameRate + 0.5);
929 }
930 mLastTimestamp = inputTimeStamp;
931
932 vpx_codec_err_t codec_return = vpx_codec_encode(mCodecContext, &raw_frame,
933 inputTimeStamp,
934 frameDuration, flags,
935 VPX_DL_REALTIME);
936 if (codec_return != VPX_CODEC_OK) {
937 ALOGE("vpx encoder failed to encode frame");
938 mSignalledError = true;
939 work->result = C2_CORRUPTED;
940 return;
941 }
942
943 bool populated = false;
944 vpx_codec_iter_t encoded_packet_iterator = nullptr;
945 const vpx_codec_cx_pkt_t* encoded_packet;
946 while ((encoded_packet = vpx_codec_get_cx_data(
947 mCodecContext, &encoded_packet_iterator))) {
948 if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) {
949 std::shared_ptr<C2LinearBlock> block;
950 C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
951 c2_status_t err = pool->fetchLinearBlock(encoded_packet->data.frame.sz, usage, &block);
952 if (err != C2_OK) {
953 ALOGE("fetchLinearBlock for Output failed with status %d", err);
954 work->result = C2_NO_MEMORY;
955 return;
956 }
957 C2WriteView wView = block->map().get();
958 if (wView.error()) {
959 ALOGE("write view map failed %d", wView.error());
960 work->result = C2_CORRUPTED;
961 return;
962 }
963
964 memcpy(wView.data(), encoded_packet->data.frame.buf, encoded_packet->data.frame.sz);
965 ++mNumInputFrames;
966
967 ALOGD("bytes generated %zu", encoded_packet->data.frame.sz);
968 uint32_t flags = 0;
969 if (eos) {
970 flags |= C2FrameData::FLAG_END_OF_STREAM;
971 }
972 work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
973 work->worklets.front()->output.buffers.clear();
974 std::shared_ptr<C2Buffer> buffer =
975 createLinearBuffer(block, 0, encoded_packet->data.frame.sz);
976 if (encoded_packet->data.frame.flags & VPX_FRAME_IS_KEY) {
977 buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
978 0u /* stream id */, C2Config::SYNC_FRAME));
979 }
980 work->worklets.front()->output.buffers.push_back(buffer);
981 work->worklets.front()->output.ordinal = work->input.ordinal;
982 work->worklets.front()->output.ordinal.timestamp = encoded_packet->data.frame.pts;
983 work->workletsProcessed = 1u;
984 populated = true;
985 if (eos) {
986 mSignalledOutputEos = true;
987 ALOGV("signalled EOS");
988 }
989 }
990 }
991 if (!populated) {
992 work->workletsProcessed = 0u;
993 }
994 }
995
drain(uint32_t drainMode,const std::shared_ptr<C2BlockPool> & pool)996 c2_status_t C2SoftVpxEnc::drain(
997 uint32_t drainMode,
998 const std::shared_ptr<C2BlockPool> &pool) {
999 (void)pool;
1000 if (drainMode == NO_DRAIN) {
1001 ALOGW("drain with NO_DRAIN: no-op");
1002 return C2_OK;
1003 }
1004 if (drainMode == DRAIN_CHAIN) {
1005 ALOGW("DRAIN_CHAIN not supported");
1006 return C2_OMITTED;
1007 }
1008
1009 return C2_OK;
1010 }
1011
1012 } // namespace android
1013