blob: 5700e5d3840e253e9527ac175ecd74987e17d3f6 [file] [log] [blame]
Pawin Vongmasa36653902018-11-15 00:10:25 -08001/*
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
32namespace android {
33
Manisha Jajooc743a112021-09-06 20:46:04 +053034C2SoftVpxEnc::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)
Manisha Jajoodf62e0f2021-11-12 18:57:28 +053062 .withDefault(new C2StreamPictureSizeInfo::input(0u, 64, 64))
Manisha Jajooc743a112021-09-06 20:46:04 +053063 .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)
Manisha Jajoodf62e0f2021-11-12 18:57:28 +053084 .withDefault(new C2StreamFrameRateInfo::output(0u, 1.))
Manisha Jajooc743a112021-09-06 20:46:04 +053085 // 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 ),
Manisha Jajoodf62e0f2021-11-12 18:57:28 +0530130 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 }),
Manisha Jajooc743a112021-09-06 20:46:04 +0530140 })
Manisha Jajoodf62e0f2021-11-12 18:57:28 +0530141 .withSetter(ProfileLevelSetter, mSize, mFrameRate, mBitrate)
Manisha Jajooc743a112021-09-06 20:46:04 +0530142 .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 })
Manisha Jajoodf62e0f2021-11-12 18:57:28 +0530155 .withSetter(ProfileLevelSetter, mSize, mFrameRate, mBitrate)
Manisha Jajooc743a112021-09-06 20:46:04 +0530156 .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
202C2R 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
211C2R 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
227C2R C2SoftVpxEnc::IntfImpl::ProfileLevelSetter(bool mayBlock,
Manisha Jajoodf62e0f2021-11-12 18:57:28 +0530228 C2P<C2StreamProfileLevelInfo::output>& me,
229 const C2P<C2StreamPictureSizeInfo::input>& size,
230 const C2P<C2StreamFrameRateInfo::output>& frameRate,
231 const C2P<C2StreamBitrateInfo::output>& bitrate) {
Manisha Jajooc743a112021-09-06 20:46:04 +0530232 (void)mayBlock;
Manisha Jajoodf62e0f2021-11-12 18:57:28 +0530233#ifdef VP9
Manisha Jajooc743a112021-09-06 20:46:04 +0530234 if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
235 me.set().profile = PROFILE_VP9_0;
236 }
Manisha Jajoodf62e0f2021-11-12 18:57:28 +0530237 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 for (const LevelLimits& limit : kLimits) {
Harish Mahendrakara786d712022-10-03 16:08:57 +0000267 if (samples <= limit.samples && samplesPerSec <= limit.samplesPerSec &&
268 bitrate.v.value <= limit.bitrate && dimension <= limit.dimension) {
269 // This is the lowest level that meets the requirements, and if
270 // we haven't seen the supplied level yet, that means we don't
271 // need the update.
272 if (needsUpdate) {
273 ALOGD("Given level %x does not cover current configuration: "
274 "adjusting to %x",
275 me.v.level, limit.level);
276 me.set().level = limit.level;
277 }
278 found = true;
279 break;
Manisha Jajoodf62e0f2021-11-12 18:57:28 +0530280 }
Manisha Jajoodf62e0f2021-11-12 18:57:28 +0530281 if (me.v.level == limit.level) {
282 // We break out of the loop when the lowest feasible level is
283 // found. The fact that we're here means that our level doesn't
284 // meet the requirement and needs to be updated.
285 needsUpdate = true;
286 }
287 }
288 if (!found) {
289 // We set to the highest supported level.
Manisha Jajooc743a112021-09-06 20:46:04 +0530290 me.set().level = LEVEL_VP9_4_1;
291 }
Manisha Jajoodf62e0f2021-11-12 18:57:28 +0530292#else
293 (void)size;
294 (void)frameRate;
295 (void)bitrate;
296 if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
297 me.set().profile = PROFILE_VP8_0;
298 }
299 if (!me.F(me.v.level).supportsAtAll(me.v.level)) {
300 me.set().level = LEVEL_UNUSED;
301 }
302#endif
Manisha Jajooc743a112021-09-06 20:46:04 +0530303 return C2R::Ok();
304}
305
306C2R C2SoftVpxEnc::IntfImpl::LayeringSetter(bool mayBlock,
307 C2P<C2StreamTemporalLayeringTuning::output>& me) {
308 (void)mayBlock;
309 C2R res = C2R::Ok();
310 if (me.v.m.layerCount > 4) {
311 me.set().m.layerCount = 4;
312 }
313 me.set().m.bLayerCount = 0;
314 // ensure ratios are monotonic and clamped between 0 and 1
315 for (size_t ix = 0; ix < me.v.flexCount(); ++ix) {
316 me.set().m.bitrateRatios[ix] = c2_clamp(
317 ix > 0 ? me.v.m.bitrateRatios[ix - 1] : 0, me.v.m.bitrateRatios[ix], 1.);
318 }
319 ALOGI("setting temporal layering %u + %u", me.v.m.layerCount, me.v.m.bLayerCount);
320 return res;
321}
322
323uint32_t C2SoftVpxEnc::IntfImpl::getSyncFramePeriod() const {
324 if (mSyncFramePeriod->value < 0 || mSyncFramePeriod->value == INT64_MAX) {
325 return 0;
326 }
327 double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value;
328 return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.);
329}
330C2R C2SoftVpxEnc::IntfImpl::ColorAspectsSetter(bool mayBlock,
331 C2P<C2StreamColorAspectsInfo::input>& me) {
332 (void)mayBlock;
333 if (me.v.range > C2Color::RANGE_OTHER) {
334 me.set().range = C2Color::RANGE_OTHER;
335 }
336 if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
337 me.set().primaries = C2Color::PRIMARIES_OTHER;
338 }
339 if (me.v.transfer > C2Color::TRANSFER_OTHER) {
340 me.set().transfer = C2Color::TRANSFER_OTHER;
341 }
342 if (me.v.matrix > C2Color::MATRIX_OTHER) {
343 me.set().matrix = C2Color::MATRIX_OTHER;
344 }
345 return C2R::Ok();
346}
347C2R C2SoftVpxEnc::IntfImpl::CodedColorAspectsSetter(
348 bool mayBlock, C2P<C2StreamColorAspectsInfo::output>& me,
349 const C2P<C2StreamColorAspectsInfo::input>& coded) {
350 (void)mayBlock;
351 me.set().range = coded.v.range;
352 me.set().primaries = coded.v.primaries;
353 me.set().transfer = coded.v.transfer;
354 me.set().matrix = coded.v.matrix;
355 return C2R::Ok();
356}
357
Pawin Vongmasa36653902018-11-15 00:10:25 -0800358#if 0
359static size_t getCpuCoreCount() {
360 long cpuCoreCount = 1;
361#if defined(_SC_NPROCESSORS_ONLN)
362 cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
363#else
364 // _SC_NPROC_ONLN must be defined...
365 cpuCoreCount = sysconf(_SC_NPROC_ONLN);
366#endif
367 CHECK(cpuCoreCount >= 1);
368 ALOGV("Number of CPU cores: %ld", cpuCoreCount);
369 return (size_t)cpuCoreCount;
370}
371#endif
372
373C2SoftVpxEnc::C2SoftVpxEnc(const char* name, c2_node_id_t id,
374 const std::shared_ptr<IntfImpl>& intfImpl)
375 : SimpleC2Component(
376 std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
377 mIntf(intfImpl),
378 mCodecContext(nullptr),
379 mCodecConfiguration(nullptr),
380 mCodecInterface(nullptr),
381 mStrideAlign(2),
382 mColorFormat(VPX_IMG_FMT_I420),
383 mBitrateControlMode(VPX_VBR),
384 mErrorResilience(false),
385 mMinQuantizer(0),
386 mMaxQuantizer(0),
387 mTemporalLayers(0),
388 mTemporalPatternType(VPXTemporalLayerPatternNone),
389 mTemporalPatternLength(0),
390 mTemporalPatternIdx(0),
391 mLastTimestamp(0x7FFFFFFFFFFFFFFFull),
392 mSignalledOutputEos(false),
393 mSignalledError(false) {
Harish Mahendrakarda612452020-03-14 17:41:29 -0700394 for (int i = 0; i < MAXTEMPORALLAYERS; i++) {
395 mTemporalLayerBitrateRatio[i] = 1.0f;
396 }
Pawin Vongmasa36653902018-11-15 00:10:25 -0800397}
398
399C2SoftVpxEnc::~C2SoftVpxEnc() {
400 onRelease();
401}
402
403c2_status_t C2SoftVpxEnc::onInit() {
404 status_t err = initEncoder();
405 return err == OK ? C2_OK : C2_CORRUPTED;
406}
407
408void C2SoftVpxEnc::onRelease() {
409 if (mCodecContext) {
410 vpx_codec_destroy(mCodecContext);
411 delete mCodecContext;
412 mCodecContext = nullptr;
413 }
414
415 if (mCodecConfiguration) {
416 delete mCodecConfiguration;
417 mCodecConfiguration = nullptr;
418 }
419
420 // this one is not allocated by us
421 mCodecInterface = nullptr;
422}
423
424c2_status_t C2SoftVpxEnc::onStop() {
425 onRelease();
426 mLastTimestamp = 0x7FFFFFFFFFFFFFFFLL;
427 mSignalledOutputEos = false;
428 mSignalledError = false;
429 return C2_OK;
430}
431
432void C2SoftVpxEnc::onReset() {
433 (void)onStop();
434}
435
436c2_status_t C2SoftVpxEnc::onFlush_sm() {
437 return onStop();
438}
439
440status_t C2SoftVpxEnc::initEncoder() {
441 vpx_codec_err_t codec_return;
442 status_t result = UNKNOWN_ERROR;
443 {
444 IntfImpl::Lock lock = mIntf->lock();
445 mSize = mIntf->getSize_l();
446 mBitrate = mIntf->getBitrate_l();
447 mBitrateMode = mIntf->getBitrateMode_l();
448 mFrameRate = mIntf->getFrameRate_l();
449 mIntraRefresh = mIntf->getIntraRefresh_l();
450 mRequestSync = mIntf->getRequestSync_l();
Harish Mahendrakarda612452020-03-14 17:41:29 -0700451 mLayering = mIntf->getTemporalLayers_l();
452 mTemporalLayers = mLayering->m.layerCount;
Pawin Vongmasa36653902018-11-15 00:10:25 -0800453 }
454
455 switch (mBitrateMode->value) {
Pawin Vongmasa36653902018-11-15 00:10:25 -0800456 case C2Config::BITRATE_CONST:
Pawin Vongmasa36653902018-11-15 00:10:25 -0800457 mBitrateControlMode = VPX_CBR;
458 break;
Harish Mahendrakar91cc4832019-06-27 11:25:58 -0700459 case C2Config::BITRATE_VARIABLE:
460 [[fallthrough]];
461 default:
462 mBitrateControlMode = VPX_VBR;
463 break;
Pawin Vongmasa36653902018-11-15 00:10:25 -0800464 }
465
466 setCodecSpecificInterface();
467 if (!mCodecInterface) goto CleanUp;
468
469 ALOGD("VPx: initEncoder. BRMode: %u. TSLayers: %zu. KF: %u. QP: %u - %u",
470 (uint32_t)mBitrateControlMode, mTemporalLayers, mIntf->getSyncFramePeriod(),
471 mMinQuantizer, mMaxQuantizer);
472
473 mCodecConfiguration = new vpx_codec_enc_cfg_t;
474 if (!mCodecConfiguration) goto CleanUp;
475 codec_return = vpx_codec_enc_config_default(mCodecInterface,
476 mCodecConfiguration,
477 0);
478 if (codec_return != VPX_CODEC_OK) {
479 ALOGE("Error populating default configuration for vpx encoder.");
480 goto CleanUp;
481 }
482
483 mCodecConfiguration->g_w = mSize->width;
484 mCodecConfiguration->g_h = mSize->height;
485 //mCodecConfiguration->g_threads = getCpuCoreCount();
486 mCodecConfiguration->g_threads = 0;
487 mCodecConfiguration->g_error_resilient = mErrorResilience;
488
489 // timebase unit is microsecond
490 // g_timebase is in seconds (i.e. 1/1000000 seconds)
491 mCodecConfiguration->g_timebase.num = 1;
492 mCodecConfiguration->g_timebase.den = 1000000;
493 // rc_target_bitrate is in kbps, mBitrate in bps
494 mCodecConfiguration->rc_target_bitrate = (mBitrate->value + 500) / 1000;
495 mCodecConfiguration->rc_end_usage = mBitrateControlMode;
496 // Disable frame drop - not allowed in MediaCodec now.
497 mCodecConfiguration->rc_dropframe_thresh = 0;
498 // Disable lagged encoding.
499 mCodecConfiguration->g_lag_in_frames = 0;
500 if (mBitrateControlMode == VPX_CBR) {
501 // Disable spatial resizing.
502 mCodecConfiguration->rc_resize_allowed = 0;
503 // Single-pass mode.
504 mCodecConfiguration->g_pass = VPX_RC_ONE_PASS;
505 // Maximum amount of bits that can be subtracted from the target
506 // bitrate - expressed as percentage of the target bitrate.
507 mCodecConfiguration->rc_undershoot_pct = 100;
508 // Maximum amount of bits that can be added to the target
509 // bitrate - expressed as percentage of the target bitrate.
510 mCodecConfiguration->rc_overshoot_pct = 15;
511 // Initial value of the buffer level in ms.
512 mCodecConfiguration->rc_buf_initial_sz = 500;
513 // Amount of data that the encoder should try to maintain in ms.
514 mCodecConfiguration->rc_buf_optimal_sz = 600;
515 // The amount of data that may be buffered by the decoding
516 // application in ms.
517 mCodecConfiguration->rc_buf_sz = 1000;
518 // Enable error resilience - needed for packet loss.
519 mCodecConfiguration->g_error_resilient = 1;
520 // Maximum key frame interval - for CBR boost to 3000
521 mCodecConfiguration->kf_max_dist = 3000;
522 // Encoder determines optimal key frame placement automatically.
523 mCodecConfiguration->kf_mode = VPX_KF_AUTO;
524 }
525
526 // Frames temporal pattern - for now WebRTC like pattern is only supported.
527 switch (mTemporalLayers) {
528 case 0:
529 mTemporalPatternLength = 0;
530 break;
531 case 1:
532 mCodecConfiguration->ts_number_layers = 1;
533 mCodecConfiguration->ts_rate_decimator[0] = 1;
534 mCodecConfiguration->ts_periodicity = 1;
535 mCodecConfiguration->ts_layer_id[0] = 0;
536 mTemporalPattern[0] = kTemporalUpdateLastRefAll;
537 mTemporalPatternLength = 1;
538 break;
539 case 2:
540 mCodecConfiguration->ts_number_layers = 2;
541 mCodecConfiguration->ts_rate_decimator[0] = 2;
542 mCodecConfiguration->ts_rate_decimator[1] = 1;
543 mCodecConfiguration->ts_periodicity = 2;
544 mCodecConfiguration->ts_layer_id[0] = 0;
545 mCodecConfiguration->ts_layer_id[1] = 1;
546 mTemporalPattern[0] = kTemporalUpdateLastAndGoldenRefAltRef;
547 mTemporalPattern[1] = kTemporalUpdateGoldenWithoutDependencyRefAltRef;
548 mTemporalPattern[2] = kTemporalUpdateLastRefAltRef;
549 mTemporalPattern[3] = kTemporalUpdateGoldenRefAltRef;
550 mTemporalPattern[4] = kTemporalUpdateLastRefAltRef;
551 mTemporalPattern[5] = kTemporalUpdateGoldenRefAltRef;
552 mTemporalPattern[6] = kTemporalUpdateLastRefAltRef;
553 mTemporalPattern[7] = kTemporalUpdateNone;
Harish Mahendrakarda612452020-03-14 17:41:29 -0700554 mTemporalLayerBitrateRatio[0] = mLayering->m.bitrateRatios[0];
Pawin Vongmasa36653902018-11-15 00:10:25 -0800555 mTemporalPatternLength = 8;
556 break;
557 case 3:
558 mCodecConfiguration->ts_number_layers = 3;
559 mCodecConfiguration->ts_rate_decimator[0] = 4;
560 mCodecConfiguration->ts_rate_decimator[1] = 2;
561 mCodecConfiguration->ts_rate_decimator[2] = 1;
562 mCodecConfiguration->ts_periodicity = 4;
563 mCodecConfiguration->ts_layer_id[0] = 0;
564 mCodecConfiguration->ts_layer_id[1] = 2;
565 mCodecConfiguration->ts_layer_id[2] = 1;
566 mCodecConfiguration->ts_layer_id[3] = 2;
567 mTemporalPattern[0] = kTemporalUpdateLastAndGoldenRefAltRef;
568 mTemporalPattern[1] = kTemporalUpdateNoneNoRefGoldenRefAltRef;
569 mTemporalPattern[2] = kTemporalUpdateGoldenWithoutDependencyRefAltRef;
570 mTemporalPattern[3] = kTemporalUpdateNone;
571 mTemporalPattern[4] = kTemporalUpdateLastRefAltRef;
572 mTemporalPattern[5] = kTemporalUpdateNone;
573 mTemporalPattern[6] = kTemporalUpdateGoldenRefAltRef;
574 mTemporalPattern[7] = kTemporalUpdateNone;
Harish Mahendrakarda612452020-03-14 17:41:29 -0700575 mTemporalLayerBitrateRatio[0] = mLayering->m.bitrateRatios[0];
576 mTemporalLayerBitrateRatio[1] = mLayering->m.bitrateRatios[1];
Pawin Vongmasa36653902018-11-15 00:10:25 -0800577 mTemporalPatternLength = 8;
578 break;
579 default:
580 ALOGE("Wrong number of temporal layers %zu", mTemporalLayers);
581 goto CleanUp;
582 }
583 // Set bitrate values for each layer
584 for (size_t i = 0; i < mCodecConfiguration->ts_number_layers; i++) {
585 mCodecConfiguration->ts_target_bitrate[i] =
586 mCodecConfiguration->rc_target_bitrate *
Harish Mahendrakarda612452020-03-14 17:41:29 -0700587 mTemporalLayerBitrateRatio[i];
Pawin Vongmasa36653902018-11-15 00:10:25 -0800588 }
589 if (mIntf->getSyncFramePeriod() >= 0) {
590 mCodecConfiguration->kf_max_dist = mIntf->getSyncFramePeriod();
591 mCodecConfiguration->kf_min_dist = mIntf->getSyncFramePeriod();
592 mCodecConfiguration->kf_mode = VPX_KF_AUTO;
593 }
594 if (mMinQuantizer > 0) {
595 mCodecConfiguration->rc_min_quantizer = mMinQuantizer;
596 }
597 if (mMaxQuantizer > 0) {
598 mCodecConfiguration->rc_max_quantizer = mMaxQuantizer;
599 }
600 setCodecSpecificConfiguration();
601 mCodecContext = new vpx_codec_ctx_t;
602 if (!mCodecContext) goto CleanUp;
603 codec_return = vpx_codec_enc_init(mCodecContext,
604 mCodecInterface,
605 mCodecConfiguration,
606 0); // flags
607 if (codec_return != VPX_CODEC_OK) {
608 ALOGE("Error initializing vpx encoder");
609 goto CleanUp;
610 }
611
612 // Extra CBR settings
613 if (mBitrateControlMode == VPX_CBR) {
614 codec_return = vpx_codec_control(mCodecContext,
615 VP8E_SET_STATIC_THRESHOLD,
616 1);
617 if (codec_return == VPX_CODEC_OK) {
618 uint32_t rc_max_intra_target =
619 (uint32_t)(mCodecConfiguration->rc_buf_optimal_sz * mFrameRate->value / 20 + 0.5);
620 // Don't go below 3 times per frame bandwidth.
621 if (rc_max_intra_target < 300) {
622 rc_max_intra_target = 300;
623 }
624 codec_return = vpx_codec_control(mCodecContext,
625 VP8E_SET_MAX_INTRA_BITRATE_PCT,
626 rc_max_intra_target);
627 }
628 if (codec_return == VPX_CODEC_OK) {
629 codec_return = vpx_codec_control(mCodecContext,
630 VP8E_SET_CPUUSED,
631 -8);
632 }
633 if (codec_return != VPX_CODEC_OK) {
634 ALOGE("Error setting cbr parameters for vpx encoder.");
635 goto CleanUp;
636 }
637 }
638
639 codec_return = setCodecSpecificControls();
640 if (codec_return != VPX_CODEC_OK) goto CleanUp;
641
642 {
643 uint32_t width = mSize->width;
644 uint32_t height = mSize->height;
645 if (((uint64_t)width * height) >
646 ((uint64_t)INT32_MAX / 3)) {
647 ALOGE("b/25812794, Buffer size is too big, width=%u, height=%u.", width, height);
648 } else {
649 uint32_t stride = (width + mStrideAlign - 1) & ~(mStrideAlign - 1);
650 uint32_t vstride = (height + mStrideAlign - 1) & ~(mStrideAlign - 1);
651 mConversionBuffer = MemoryBlock::Allocate(stride * vstride * 3 / 2);
652 if (!mConversionBuffer.size()) {
653 ALOGE("Allocating conversion buffer failed.");
654 } else {
655 mNumInputFrames = -1;
656 return OK;
657 }
658 }
659 }
660
661CleanUp:
662 onRelease();
663 return result;
664}
665
666vpx_enc_frame_flags_t C2SoftVpxEnc::getEncodeFlags() {
667 vpx_enc_frame_flags_t flags = 0;
668 if (mTemporalPatternLength > 0) {
669 int patternIdx = mTemporalPatternIdx % mTemporalPatternLength;
670 mTemporalPatternIdx++;
671 switch (mTemporalPattern[patternIdx]) {
672 case kTemporalUpdateLast:
673 flags |= VP8_EFLAG_NO_UPD_GF;
674 flags |= VP8_EFLAG_NO_UPD_ARF;
675 flags |= VP8_EFLAG_NO_REF_GF;
676 flags |= VP8_EFLAG_NO_REF_ARF;
677 break;
678 case kTemporalUpdateGoldenWithoutDependency:
679 flags |= VP8_EFLAG_NO_REF_GF;
680 [[fallthrough]];
681 case kTemporalUpdateGolden:
682 flags |= VP8_EFLAG_NO_REF_ARF;
683 flags |= VP8_EFLAG_NO_UPD_ARF;
684 flags |= VP8_EFLAG_NO_UPD_LAST;
685 break;
686 case kTemporalUpdateAltrefWithoutDependency:
687 flags |= VP8_EFLAG_NO_REF_ARF;
688 flags |= VP8_EFLAG_NO_REF_GF;
689 [[fallthrough]];
690 case kTemporalUpdateAltref:
691 flags |= VP8_EFLAG_NO_UPD_GF;
692 flags |= VP8_EFLAG_NO_UPD_LAST;
693 break;
694 case kTemporalUpdateNoneNoRefAltref:
695 flags |= VP8_EFLAG_NO_REF_ARF;
696 [[fallthrough]];
697 case kTemporalUpdateNone:
698 flags |= VP8_EFLAG_NO_UPD_GF;
699 flags |= VP8_EFLAG_NO_UPD_ARF;
700 flags |= VP8_EFLAG_NO_UPD_LAST;
701 flags |= VP8_EFLAG_NO_UPD_ENTROPY;
702 break;
703 case kTemporalUpdateNoneNoRefGoldenRefAltRef:
704 flags |= VP8_EFLAG_NO_REF_GF;
705 flags |= VP8_EFLAG_NO_UPD_GF;
706 flags |= VP8_EFLAG_NO_UPD_ARF;
707 flags |= VP8_EFLAG_NO_UPD_LAST;
708 flags |= VP8_EFLAG_NO_UPD_ENTROPY;
709 break;
710 case kTemporalUpdateGoldenWithoutDependencyRefAltRef:
711 flags |= VP8_EFLAG_NO_REF_GF;
712 flags |= VP8_EFLAG_NO_UPD_ARF;
713 flags |= VP8_EFLAG_NO_UPD_LAST;
714 break;
715 case kTemporalUpdateLastRefAltRef:
716 flags |= VP8_EFLAG_NO_UPD_GF;
717 flags |= VP8_EFLAG_NO_UPD_ARF;
718 flags |= VP8_EFLAG_NO_REF_GF;
719 break;
720 case kTemporalUpdateGoldenRefAltRef:
721 flags |= VP8_EFLAG_NO_UPD_ARF;
722 flags |= VP8_EFLAG_NO_UPD_LAST;
723 break;
724 case kTemporalUpdateLastAndGoldenRefAltRef:
725 flags |= VP8_EFLAG_NO_UPD_ARF;
726 flags |= VP8_EFLAG_NO_REF_GF;
727 break;
728 case kTemporalUpdateLastRefAll:
729 flags |= VP8_EFLAG_NO_UPD_ARF;
730 flags |= VP8_EFLAG_NO_UPD_GF;
731 break;
732 }
733 }
734 return flags;
735}
736
737// TODO: add support for YUV input color formats
738// TODO: add support for SVC, ARF. SVC and ARF returns multiple frames
739// (hierarchical / noshow) in one call. These frames should be combined in to
740// a single buffer and sent back to the client
741void C2SoftVpxEnc::process(
742 const std::unique_ptr<C2Work> &work,
743 const std::shared_ptr<C2BlockPool> &pool) {
744 // Initialize output work
745 work->result = C2_OK;
746 work->workletsProcessed = 1u;
747 work->worklets.front()->output.flags = work->input.flags;
748
749 if (mSignalledError || mSignalledOutputEos) {
750 work->result = C2_BAD_VALUE;
751 return;
752 }
753 // Initialize encoder if not already
754 if (!mCodecContext && OK != initEncoder()) {
755 ALOGE("Failed to initialize encoder");
756 mSignalledError = true;
757 work->result = C2_CORRUPTED;
758 return;
759 }
760
Patryk Bussec2607552022-09-05 11:33:07 +0000761 std::shared_ptr<C2GraphicView> rView;
Pawin Vongmasa36653902018-11-15 00:10:25 -0800762 std::shared_ptr<C2Buffer> inputBuffer;
763 if (!work->input.buffers.empty()) {
764 inputBuffer = work->input.buffers[0];
Patryk Bussec2607552022-09-05 11:33:07 +0000765 rView = std::make_shared<C2GraphicView>(
Pawin Vongmasa36653902018-11-15 00:10:25 -0800766 inputBuffer->data().graphicBlocks().front().map().get());
767 if (rView->error() != C2_OK) {
768 ALOGE("graphic view map err = %d", rView->error());
769 work->result = C2_CORRUPTED;
770 return;
771 }
Patryk Bussec2607552022-09-05 11:33:07 +0000772 //(b/232396154)
773 //workaround for incorrect crop size in view when using surface mode
774 rView->setCrop_be(C2Rect(mSize->width, mSize->height));
Pawin Vongmasa36653902018-11-15 00:10:25 -0800775 } else {
776 ALOGV("Empty input Buffer");
777 uint32_t flags = 0;
778 if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
779 flags |= C2FrameData::FLAG_END_OF_STREAM;
780 }
781 work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
782 work->worklets.front()->output.buffers.clear();
783 work->worklets.front()->output.ordinal = work->input.ordinal;
784 work->workletsProcessed = 1u;
785 return;
786 }
787
788 const C2ConstGraphicBlock inBuffer =
789 inputBuffer->data().graphicBlocks().front();
Harish Mahendrakar66e98bd2020-06-09 07:45:33 +0530790 if (inBuffer.width() < mSize->width ||
791 inBuffer.height() < mSize->height) {
Pawin Vongmasa36653902018-11-15 00:10:25 -0800792 ALOGE("unexpected Input buffer attributes %d(%d) x %d(%d)",
793 inBuffer.width(), mSize->width, inBuffer.height(),
794 mSize->height);
795 mSignalledError = true;
796 work->result = C2_BAD_VALUE;
797 return;
798 }
799 bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
800 vpx_image_t raw_frame;
801 const C2PlanarLayout &layout = rView->layout();
Harish Mahendrakar66e98bd2020-06-09 07:45:33 +0530802 uint32_t width = mSize->width;
803 uint32_t height = mSize->height;
Pawin Vongmasa36653902018-11-15 00:10:25 -0800804 if (width > 0x8000 || height > 0x8000) {
805 ALOGE("Image too big: %u x %u", width, height);
806 work->result = C2_BAD_VALUE;
807 return;
808 }
809 uint32_t stride = (width + mStrideAlign - 1) & ~(mStrideAlign - 1);
810 uint32_t vstride = (height + mStrideAlign - 1) & ~(mStrideAlign - 1);
811 switch (layout.type) {
812 case C2PlanarLayout::TYPE_RGB:
813 case C2PlanarLayout::TYPE_RGBA: {
Manisha Jajood9f98442021-06-02 11:27:07 +0530814 std::shared_ptr<C2StreamColorAspectsInfo::output> colorAspects;
815 {
816 IntfImpl::Lock lock = mIntf->lock();
817 colorAspects = mIntf->getCodedColorAspects_l();
818 }
Pawin Vongmasa36653902018-11-15 00:10:25 -0800819 ConvertRGBToPlanarYUV(mConversionBuffer.data(), stride, vstride,
Manisha Jajood9f98442021-06-02 11:27:07 +0530820 mConversionBuffer.size(), *rView.get(),
821 colorAspects->matrix, colorAspects->range);
Pawin Vongmasa36653902018-11-15 00:10:25 -0800822 vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, width, height,
823 mStrideAlign, mConversionBuffer.data());
824 break;
825 }
826 case C2PlanarLayout::TYPE_YUV: {
827 if (!IsYUV420(*rView)) {
828 ALOGE("input is not YUV420");
829 work->result = C2_BAD_VALUE;
830 return;
831 }
832
833 if (layout.planes[layout.PLANE_Y].colInc == 1
834 && layout.planes[layout.PLANE_U].colInc == 1
835 && layout.planes[layout.PLANE_V].colInc == 1) {
836 // I420 compatible - though with custom offset and stride
837 vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, width, height,
838 mStrideAlign, (uint8_t*)rView->data()[0]);
839 raw_frame.planes[1] = (uint8_t*)rView->data()[1];
840 raw_frame.planes[2] = (uint8_t*)rView->data()[2];
841 raw_frame.stride[0] = layout.planes[layout.PLANE_Y].rowInc;
842 raw_frame.stride[1] = layout.planes[layout.PLANE_U].rowInc;
843 raw_frame.stride[2] = layout.planes[layout.PLANE_V].rowInc;
844 } else {
845 // copy to I420
846 MediaImage2 img = CreateYUV420PlanarMediaImage2(width, height, stride, vstride);
847 if (mConversionBuffer.size() >= stride * vstride * 3 / 2) {
848 status_t err = ImageCopy(mConversionBuffer.data(), &img, *rView);
849 if (err != OK) {
850 ALOGE("Buffer conversion failed: %d", err);
851 work->result = C2_BAD_VALUE;
852 return;
853 }
854 vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, stride, vstride,
Liu, Kai1386c36f2019-08-22 13:37:41 +0800855 mStrideAlign, mConversionBuffer.data());
Pawin Vongmasa36653902018-11-15 00:10:25 -0800856 vpx_img_set_rect(&raw_frame, 0, 0, width, height);
857 } else {
858 ALOGE("Conversion buffer is too small: %u x %u for %zu",
859 stride, vstride, mConversionBuffer.size());
860 work->result = C2_BAD_VALUE;
861 return;
862 }
863 }
864 break;
865 }
866 default:
867 ALOGE("Unrecognized plane type: %d", layout.type);
868 work->result = C2_BAD_VALUE;
869 return;
870 }
871
872 vpx_enc_frame_flags_t flags = getEncodeFlags();
873 // handle dynamic config parameters
874 {
875 IntfImpl::Lock lock = mIntf->lock();
876 std::shared_ptr<C2StreamIntraRefreshTuning::output> intraRefresh = mIntf->getIntraRefresh_l();
877 std::shared_ptr<C2StreamBitrateInfo::output> bitrate = mIntf->getBitrate_l();
878 std::shared_ptr<C2StreamRequestSyncFrameTuning::output> requestSync = mIntf->getRequestSync_l();
879 lock.unlock();
880
881 if (intraRefresh != mIntraRefresh) {
882 mIntraRefresh = intraRefresh;
883 ALOGV("Got mIntraRefresh request");
884 }
885
886 if (requestSync != mRequestSync) {
887 // we can handle IDR immediately
888 if (requestSync->value) {
889 // unset request
890 C2StreamRequestSyncFrameTuning::output clearSync(0u, C2_FALSE);
891 std::vector<std::unique_ptr<C2SettingResult>> failures;
892 mIntf->config({ &clearSync }, C2_MAY_BLOCK, &failures);
893 ALOGV("Got sync request");
894 flags |= VPX_EFLAG_FORCE_KF;
895 }
896 mRequestSync = requestSync;
897 }
898
899 if (bitrate != mBitrate) {
900 mBitrate = bitrate;
901 mCodecConfiguration->rc_target_bitrate =
902 (mBitrate->value + 500) / 1000;
903 vpx_codec_err_t res = vpx_codec_enc_config_set(mCodecContext,
904 mCodecConfiguration);
905 if (res != VPX_CODEC_OK) {
906 ALOGE("vpx encoder failed to update bitrate: %s",
907 vpx_codec_err_to_string(res));
908 mSignalledError = true;
909 work->result = C2_CORRUPTED;
910 return;
911 }
912 }
913 }
914
915 uint64_t inputTimeStamp = work->input.ordinal.timestamp.peekull();
916 uint32_t frameDuration;
917 if (inputTimeStamp > mLastTimestamp) {
918 frameDuration = (uint32_t)(inputTimeStamp - mLastTimestamp);
919 } else {
920 // Use default of 30 fps in case of 0 frame rate.
921 float frameRate = mFrameRate->value;
922 if (frameRate < 0.001) {
923 frameRate = 30;
924 }
925 frameDuration = (uint32_t)(1000000 / frameRate + 0.5);
926 }
927 mLastTimestamp = inputTimeStamp;
928
929 vpx_codec_err_t codec_return = vpx_codec_encode(mCodecContext, &raw_frame,
930 inputTimeStamp,
931 frameDuration, flags,
932 VPX_DL_REALTIME);
933 if (codec_return != VPX_CODEC_OK) {
934 ALOGE("vpx encoder failed to encode frame");
935 mSignalledError = true;
936 work->result = C2_CORRUPTED;
937 return;
938 }
939
940 bool populated = false;
941 vpx_codec_iter_t encoded_packet_iterator = nullptr;
942 const vpx_codec_cx_pkt_t* encoded_packet;
943 while ((encoded_packet = vpx_codec_get_cx_data(
944 mCodecContext, &encoded_packet_iterator))) {
945 if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) {
946 std::shared_ptr<C2LinearBlock> block;
947 C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
948 c2_status_t err = pool->fetchLinearBlock(encoded_packet->data.frame.sz, usage, &block);
949 if (err != C2_OK) {
950 ALOGE("fetchLinearBlock for Output failed with status %d", err);
951 work->result = C2_NO_MEMORY;
952 return;
953 }
954 C2WriteView wView = block->map().get();
955 if (wView.error()) {
956 ALOGE("write view map failed %d", wView.error());
957 work->result = C2_CORRUPTED;
958 return;
959 }
960
961 memcpy(wView.data(), encoded_packet->data.frame.buf, encoded_packet->data.frame.sz);
962 ++mNumInputFrames;
963
964 ALOGD("bytes generated %zu", encoded_packet->data.frame.sz);
965 uint32_t flags = 0;
966 if (eos) {
967 flags |= C2FrameData::FLAG_END_OF_STREAM;
968 }
969 work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
970 work->worklets.front()->output.buffers.clear();
Lajos Molnar2d83c002021-06-09 16:25:07 -0700971 std::shared_ptr<C2Buffer> buffer =
972 createLinearBuffer(block, 0, encoded_packet->data.frame.sz);
Pawin Vongmasa36653902018-11-15 00:10:25 -0800973 if (encoded_packet->data.frame.flags & VPX_FRAME_IS_KEY) {
974 buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
Lajos Molnar3bb81cd2019-02-20 15:10:30 -0800975 0u /* stream id */, C2Config::SYNC_FRAME));
Pawin Vongmasa36653902018-11-15 00:10:25 -0800976 }
977 work->worklets.front()->output.buffers.push_back(buffer);
978 work->worklets.front()->output.ordinal = work->input.ordinal;
979 work->worklets.front()->output.ordinal.timestamp = encoded_packet->data.frame.pts;
980 work->workletsProcessed = 1u;
981 populated = true;
982 if (eos) {
983 mSignalledOutputEos = true;
984 ALOGV("signalled EOS");
985 }
986 }
987 }
988 if (!populated) {
989 work->workletsProcessed = 0u;
990 }
991}
992
993c2_status_t C2SoftVpxEnc::drain(
994 uint32_t drainMode,
995 const std::shared_ptr<C2BlockPool> &pool) {
996 (void)pool;
997 if (drainMode == NO_DRAIN) {
998 ALOGW("drain with NO_DRAIN: no-op");
999 return C2_OK;
1000 }
1001 if (drainMode == DRAIN_CHAIN) {
1002 ALOGW("DRAIN_CHAIN not supported");
1003 return C2_OMITTED;
1004 }
1005
1006 return C2_OK;
1007}
1008
1009} // namespace android