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
34 #if 0
35 static size_t getCpuCoreCount() {
36 long cpuCoreCount = 1;
37 #if defined(_SC_NPROCESSORS_ONLN)
38 cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
39 #else
40 // _SC_NPROC_ONLN must be defined...
41 cpuCoreCount = sysconf(_SC_NPROC_ONLN);
42 #endif
43 CHECK(cpuCoreCount >= 1);
44 ALOGV("Number of CPU cores: %ld", cpuCoreCount);
45 return (size_t)cpuCoreCount;
46 }
47 #endif
48
C2SoftVpxEnc(const char * name,c2_node_id_t id,const std::shared_ptr<IntfImpl> & intfImpl)49 C2SoftVpxEnc::C2SoftVpxEnc(const char* name, c2_node_id_t id,
50 const std::shared_ptr<IntfImpl>& intfImpl)
51 : SimpleC2Component(
52 std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
53 mIntf(intfImpl),
54 mCodecContext(nullptr),
55 mCodecConfiguration(nullptr),
56 mCodecInterface(nullptr),
57 mStrideAlign(2),
58 mColorFormat(VPX_IMG_FMT_I420),
59 mBitrateControlMode(VPX_VBR),
60 mErrorResilience(false),
61 mMinQuantizer(0),
62 mMaxQuantizer(0),
63 mTemporalLayers(0),
64 mTemporalPatternType(VPXTemporalLayerPatternNone),
65 mTemporalPatternLength(0),
66 mTemporalPatternIdx(0),
67 mLastTimestamp(0x7FFFFFFFFFFFFFFFull),
68 mSignalledOutputEos(false),
69 mSignalledError(false) {
70 memset(mTemporalLayerBitrateRatio, 0, sizeof(mTemporalLayerBitrateRatio));
71 mTemporalLayerBitrateRatio[0] = 100;
72 }
73
~C2SoftVpxEnc()74 C2SoftVpxEnc::~C2SoftVpxEnc() {
75 onRelease();
76 }
77
onInit()78 c2_status_t C2SoftVpxEnc::onInit() {
79 status_t err = initEncoder();
80 return err == OK ? C2_OK : C2_CORRUPTED;
81 }
82
onRelease()83 void C2SoftVpxEnc::onRelease() {
84 if (mCodecContext) {
85 vpx_codec_destroy(mCodecContext);
86 delete mCodecContext;
87 mCodecContext = nullptr;
88 }
89
90 if (mCodecConfiguration) {
91 delete mCodecConfiguration;
92 mCodecConfiguration = nullptr;
93 }
94
95 // this one is not allocated by us
96 mCodecInterface = nullptr;
97 }
98
onStop()99 c2_status_t C2SoftVpxEnc::onStop() {
100 onRelease();
101 mLastTimestamp = 0x7FFFFFFFFFFFFFFFLL;
102 mSignalledOutputEos = false;
103 mSignalledError = false;
104 return C2_OK;
105 }
106
onReset()107 void C2SoftVpxEnc::onReset() {
108 (void)onStop();
109 }
110
onFlush_sm()111 c2_status_t C2SoftVpxEnc::onFlush_sm() {
112 return onStop();
113 }
114
initEncoder()115 status_t C2SoftVpxEnc::initEncoder() {
116 vpx_codec_err_t codec_return;
117 status_t result = UNKNOWN_ERROR;
118 {
119 IntfImpl::Lock lock = mIntf->lock();
120 mSize = mIntf->getSize_l();
121 mBitrate = mIntf->getBitrate_l();
122 mBitrateMode = mIntf->getBitrateMode_l();
123 mFrameRate = mIntf->getFrameRate_l();
124 mIntraRefresh = mIntf->getIntraRefresh_l();
125 mRequestSync = mIntf->getRequestSync_l();
126 mTemporalLayers = mIntf->getTemporalLayers_l()->m.layerCount;
127 }
128
129 switch (mBitrateMode->value) {
130 case C2Config::BITRATE_VARIABLE:
131 mBitrateControlMode = VPX_VBR;
132 break;
133 case C2Config::BITRATE_CONST:
134 default:
135 mBitrateControlMode = VPX_CBR;
136 break;
137 break;
138 }
139
140 setCodecSpecificInterface();
141 if (!mCodecInterface) goto CleanUp;
142
143 ALOGD("VPx: initEncoder. BRMode: %u. TSLayers: %zu. KF: %u. QP: %u - %u",
144 (uint32_t)mBitrateControlMode, mTemporalLayers, mIntf->getSyncFramePeriod(),
145 mMinQuantizer, mMaxQuantizer);
146
147 mCodecConfiguration = new vpx_codec_enc_cfg_t;
148 if (!mCodecConfiguration) goto CleanUp;
149 codec_return = vpx_codec_enc_config_default(mCodecInterface,
150 mCodecConfiguration,
151 0);
152 if (codec_return != VPX_CODEC_OK) {
153 ALOGE("Error populating default configuration for vpx encoder.");
154 goto CleanUp;
155 }
156
157 mCodecConfiguration->g_w = mSize->width;
158 mCodecConfiguration->g_h = mSize->height;
159 //mCodecConfiguration->g_threads = getCpuCoreCount();
160 mCodecConfiguration->g_threads = 0;
161 mCodecConfiguration->g_error_resilient = mErrorResilience;
162
163 // timebase unit is microsecond
164 // g_timebase is in seconds (i.e. 1/1000000 seconds)
165 mCodecConfiguration->g_timebase.num = 1;
166 mCodecConfiguration->g_timebase.den = 1000000;
167 // rc_target_bitrate is in kbps, mBitrate in bps
168 mCodecConfiguration->rc_target_bitrate = (mBitrate->value + 500) / 1000;
169 mCodecConfiguration->rc_end_usage = mBitrateControlMode;
170 // Disable frame drop - not allowed in MediaCodec now.
171 mCodecConfiguration->rc_dropframe_thresh = 0;
172 // Disable lagged encoding.
173 mCodecConfiguration->g_lag_in_frames = 0;
174 if (mBitrateControlMode == VPX_CBR) {
175 // Disable spatial resizing.
176 mCodecConfiguration->rc_resize_allowed = 0;
177 // Single-pass mode.
178 mCodecConfiguration->g_pass = VPX_RC_ONE_PASS;
179 // Maximum amount of bits that can be subtracted from the target
180 // bitrate - expressed as percentage of the target bitrate.
181 mCodecConfiguration->rc_undershoot_pct = 100;
182 // Maximum amount of bits that can be added to the target
183 // bitrate - expressed as percentage of the target bitrate.
184 mCodecConfiguration->rc_overshoot_pct = 15;
185 // Initial value of the buffer level in ms.
186 mCodecConfiguration->rc_buf_initial_sz = 500;
187 // Amount of data that the encoder should try to maintain in ms.
188 mCodecConfiguration->rc_buf_optimal_sz = 600;
189 // The amount of data that may be buffered by the decoding
190 // application in ms.
191 mCodecConfiguration->rc_buf_sz = 1000;
192 // Enable error resilience - needed for packet loss.
193 mCodecConfiguration->g_error_resilient = 1;
194 // Maximum key frame interval - for CBR boost to 3000
195 mCodecConfiguration->kf_max_dist = 3000;
196 // Encoder determines optimal key frame placement automatically.
197 mCodecConfiguration->kf_mode = VPX_KF_AUTO;
198 }
199
200 // Frames temporal pattern - for now WebRTC like pattern is only supported.
201 switch (mTemporalLayers) {
202 case 0:
203 mTemporalPatternLength = 0;
204 break;
205 case 1:
206 mCodecConfiguration->ts_number_layers = 1;
207 mCodecConfiguration->ts_rate_decimator[0] = 1;
208 mCodecConfiguration->ts_periodicity = 1;
209 mCodecConfiguration->ts_layer_id[0] = 0;
210 mTemporalPattern[0] = kTemporalUpdateLastRefAll;
211 mTemporalPatternLength = 1;
212 break;
213 case 2:
214 mCodecConfiguration->ts_number_layers = 2;
215 mCodecConfiguration->ts_rate_decimator[0] = 2;
216 mCodecConfiguration->ts_rate_decimator[1] = 1;
217 mCodecConfiguration->ts_periodicity = 2;
218 mCodecConfiguration->ts_layer_id[0] = 0;
219 mCodecConfiguration->ts_layer_id[1] = 1;
220 mTemporalPattern[0] = kTemporalUpdateLastAndGoldenRefAltRef;
221 mTemporalPattern[1] = kTemporalUpdateGoldenWithoutDependencyRefAltRef;
222 mTemporalPattern[2] = kTemporalUpdateLastRefAltRef;
223 mTemporalPattern[3] = kTemporalUpdateGoldenRefAltRef;
224 mTemporalPattern[4] = kTemporalUpdateLastRefAltRef;
225 mTemporalPattern[5] = kTemporalUpdateGoldenRefAltRef;
226 mTemporalPattern[6] = kTemporalUpdateLastRefAltRef;
227 mTemporalPattern[7] = kTemporalUpdateNone;
228 mTemporalPatternLength = 8;
229 break;
230 case 3:
231 mCodecConfiguration->ts_number_layers = 3;
232 mCodecConfiguration->ts_rate_decimator[0] = 4;
233 mCodecConfiguration->ts_rate_decimator[1] = 2;
234 mCodecConfiguration->ts_rate_decimator[2] = 1;
235 mCodecConfiguration->ts_periodicity = 4;
236 mCodecConfiguration->ts_layer_id[0] = 0;
237 mCodecConfiguration->ts_layer_id[1] = 2;
238 mCodecConfiguration->ts_layer_id[2] = 1;
239 mCodecConfiguration->ts_layer_id[3] = 2;
240 mTemporalPattern[0] = kTemporalUpdateLastAndGoldenRefAltRef;
241 mTemporalPattern[1] = kTemporalUpdateNoneNoRefGoldenRefAltRef;
242 mTemporalPattern[2] = kTemporalUpdateGoldenWithoutDependencyRefAltRef;
243 mTemporalPattern[3] = kTemporalUpdateNone;
244 mTemporalPattern[4] = kTemporalUpdateLastRefAltRef;
245 mTemporalPattern[5] = kTemporalUpdateNone;
246 mTemporalPattern[6] = kTemporalUpdateGoldenRefAltRef;
247 mTemporalPattern[7] = kTemporalUpdateNone;
248 mTemporalPatternLength = 8;
249 break;
250 default:
251 ALOGE("Wrong number of temporal layers %zu", mTemporalLayers);
252 goto CleanUp;
253 }
254 // Set bitrate values for each layer
255 for (size_t i = 0; i < mCodecConfiguration->ts_number_layers; i++) {
256 mCodecConfiguration->ts_target_bitrate[i] =
257 mCodecConfiguration->rc_target_bitrate *
258 mTemporalLayerBitrateRatio[i] / 100;
259 }
260 if (mIntf->getSyncFramePeriod() >= 0) {
261 mCodecConfiguration->kf_max_dist = mIntf->getSyncFramePeriod();
262 mCodecConfiguration->kf_min_dist = mIntf->getSyncFramePeriod();
263 mCodecConfiguration->kf_mode = VPX_KF_AUTO;
264 }
265 if (mMinQuantizer > 0) {
266 mCodecConfiguration->rc_min_quantizer = mMinQuantizer;
267 }
268 if (mMaxQuantizer > 0) {
269 mCodecConfiguration->rc_max_quantizer = mMaxQuantizer;
270 }
271 setCodecSpecificConfiguration();
272 mCodecContext = new vpx_codec_ctx_t;
273 if (!mCodecContext) goto CleanUp;
274 codec_return = vpx_codec_enc_init(mCodecContext,
275 mCodecInterface,
276 mCodecConfiguration,
277 0); // flags
278 if (codec_return != VPX_CODEC_OK) {
279 ALOGE("Error initializing vpx encoder");
280 goto CleanUp;
281 }
282
283 // Extra CBR settings
284 if (mBitrateControlMode == VPX_CBR) {
285 codec_return = vpx_codec_control(mCodecContext,
286 VP8E_SET_STATIC_THRESHOLD,
287 1);
288 if (codec_return == VPX_CODEC_OK) {
289 uint32_t rc_max_intra_target =
290 (uint32_t)(mCodecConfiguration->rc_buf_optimal_sz * mFrameRate->value / 20 + 0.5);
291 // Don't go below 3 times per frame bandwidth.
292 if (rc_max_intra_target < 300) {
293 rc_max_intra_target = 300;
294 }
295 codec_return = vpx_codec_control(mCodecContext,
296 VP8E_SET_MAX_INTRA_BITRATE_PCT,
297 rc_max_intra_target);
298 }
299 if (codec_return == VPX_CODEC_OK) {
300 codec_return = vpx_codec_control(mCodecContext,
301 VP8E_SET_CPUUSED,
302 -8);
303 }
304 if (codec_return != VPX_CODEC_OK) {
305 ALOGE("Error setting cbr parameters for vpx encoder.");
306 goto CleanUp;
307 }
308 }
309
310 codec_return = setCodecSpecificControls();
311 if (codec_return != VPX_CODEC_OK) goto CleanUp;
312
313 {
314 uint32_t width = mSize->width;
315 uint32_t height = mSize->height;
316 if (((uint64_t)width * height) >
317 ((uint64_t)INT32_MAX / 3)) {
318 ALOGE("b/25812794, Buffer size is too big, width=%u, height=%u.", width, height);
319 } else {
320 uint32_t stride = (width + mStrideAlign - 1) & ~(mStrideAlign - 1);
321 uint32_t vstride = (height + mStrideAlign - 1) & ~(mStrideAlign - 1);
322 mConversionBuffer = MemoryBlock::Allocate(stride * vstride * 3 / 2);
323 if (!mConversionBuffer.size()) {
324 ALOGE("Allocating conversion buffer failed.");
325 } else {
326 mNumInputFrames = -1;
327 return OK;
328 }
329 }
330 }
331
332 CleanUp:
333 onRelease();
334 return result;
335 }
336
getEncodeFlags()337 vpx_enc_frame_flags_t C2SoftVpxEnc::getEncodeFlags() {
338 vpx_enc_frame_flags_t flags = 0;
339 if (mTemporalPatternLength > 0) {
340 int patternIdx = mTemporalPatternIdx % mTemporalPatternLength;
341 mTemporalPatternIdx++;
342 switch (mTemporalPattern[patternIdx]) {
343 case kTemporalUpdateLast:
344 flags |= VP8_EFLAG_NO_UPD_GF;
345 flags |= VP8_EFLAG_NO_UPD_ARF;
346 flags |= VP8_EFLAG_NO_REF_GF;
347 flags |= VP8_EFLAG_NO_REF_ARF;
348 break;
349 case kTemporalUpdateGoldenWithoutDependency:
350 flags |= VP8_EFLAG_NO_REF_GF;
351 [[fallthrough]];
352 case kTemporalUpdateGolden:
353 flags |= VP8_EFLAG_NO_REF_ARF;
354 flags |= VP8_EFLAG_NO_UPD_ARF;
355 flags |= VP8_EFLAG_NO_UPD_LAST;
356 break;
357 case kTemporalUpdateAltrefWithoutDependency:
358 flags |= VP8_EFLAG_NO_REF_ARF;
359 flags |= VP8_EFLAG_NO_REF_GF;
360 [[fallthrough]];
361 case kTemporalUpdateAltref:
362 flags |= VP8_EFLAG_NO_UPD_GF;
363 flags |= VP8_EFLAG_NO_UPD_LAST;
364 break;
365 case kTemporalUpdateNoneNoRefAltref:
366 flags |= VP8_EFLAG_NO_REF_ARF;
367 [[fallthrough]];
368 case kTemporalUpdateNone:
369 flags |= VP8_EFLAG_NO_UPD_GF;
370 flags |= VP8_EFLAG_NO_UPD_ARF;
371 flags |= VP8_EFLAG_NO_UPD_LAST;
372 flags |= VP8_EFLAG_NO_UPD_ENTROPY;
373 break;
374 case kTemporalUpdateNoneNoRefGoldenRefAltRef:
375 flags |= VP8_EFLAG_NO_REF_GF;
376 flags |= VP8_EFLAG_NO_UPD_GF;
377 flags |= VP8_EFLAG_NO_UPD_ARF;
378 flags |= VP8_EFLAG_NO_UPD_LAST;
379 flags |= VP8_EFLAG_NO_UPD_ENTROPY;
380 break;
381 case kTemporalUpdateGoldenWithoutDependencyRefAltRef:
382 flags |= VP8_EFLAG_NO_REF_GF;
383 flags |= VP8_EFLAG_NO_UPD_ARF;
384 flags |= VP8_EFLAG_NO_UPD_LAST;
385 break;
386 case kTemporalUpdateLastRefAltRef:
387 flags |= VP8_EFLAG_NO_UPD_GF;
388 flags |= VP8_EFLAG_NO_UPD_ARF;
389 flags |= VP8_EFLAG_NO_REF_GF;
390 break;
391 case kTemporalUpdateGoldenRefAltRef:
392 flags |= VP8_EFLAG_NO_UPD_ARF;
393 flags |= VP8_EFLAG_NO_UPD_LAST;
394 break;
395 case kTemporalUpdateLastAndGoldenRefAltRef:
396 flags |= VP8_EFLAG_NO_UPD_ARF;
397 flags |= VP8_EFLAG_NO_REF_GF;
398 break;
399 case kTemporalUpdateLastRefAll:
400 flags |= VP8_EFLAG_NO_UPD_ARF;
401 flags |= VP8_EFLAG_NO_UPD_GF;
402 break;
403 }
404 }
405 return flags;
406 }
407
408 // TODO: add support for YUV input color formats
409 // TODO: add support for SVC, ARF. SVC and ARF returns multiple frames
410 // (hierarchical / noshow) in one call. These frames should be combined in to
411 // a single buffer and sent back to the client
process(const std::unique_ptr<C2Work> & work,const std::shared_ptr<C2BlockPool> & pool)412 void C2SoftVpxEnc::process(
413 const std::unique_ptr<C2Work> &work,
414 const std::shared_ptr<C2BlockPool> &pool) {
415 // Initialize output work
416 work->result = C2_OK;
417 work->workletsProcessed = 1u;
418 work->worklets.front()->output.flags = work->input.flags;
419
420 if (mSignalledError || mSignalledOutputEos) {
421 work->result = C2_BAD_VALUE;
422 return;
423 }
424 // Initialize encoder if not already
425 if (!mCodecContext && OK != initEncoder()) {
426 ALOGE("Failed to initialize encoder");
427 mSignalledError = true;
428 work->result = C2_CORRUPTED;
429 return;
430 }
431
432 std::shared_ptr<const C2GraphicView> rView;
433 std::shared_ptr<C2Buffer> inputBuffer;
434 if (!work->input.buffers.empty()) {
435 inputBuffer = work->input.buffers[0];
436 rView = std::make_shared<const C2GraphicView>(
437 inputBuffer->data().graphicBlocks().front().map().get());
438 if (rView->error() != C2_OK) {
439 ALOGE("graphic view map err = %d", rView->error());
440 work->result = C2_CORRUPTED;
441 return;
442 }
443 } else {
444 ALOGV("Empty input Buffer");
445 uint32_t flags = 0;
446 if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
447 flags |= C2FrameData::FLAG_END_OF_STREAM;
448 }
449 work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
450 work->worklets.front()->output.buffers.clear();
451 work->worklets.front()->output.ordinal = work->input.ordinal;
452 work->workletsProcessed = 1u;
453 return;
454 }
455
456 const C2ConstGraphicBlock inBuffer =
457 inputBuffer->data().graphicBlocks().front();
458 if (inBuffer.width() != mSize->width ||
459 inBuffer.height() != mSize->height) {
460 ALOGE("unexpected Input buffer attributes %d(%d) x %d(%d)",
461 inBuffer.width(), mSize->width, inBuffer.height(),
462 mSize->height);
463 mSignalledError = true;
464 work->result = C2_BAD_VALUE;
465 return;
466 }
467 bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
468 vpx_image_t raw_frame;
469 const C2PlanarLayout &layout = rView->layout();
470 uint32_t width = rView->width();
471 uint32_t height = rView->height();
472 if (width > 0x8000 || height > 0x8000) {
473 ALOGE("Image too big: %u x %u", width, height);
474 work->result = C2_BAD_VALUE;
475 return;
476 }
477 uint32_t stride = (width + mStrideAlign - 1) & ~(mStrideAlign - 1);
478 uint32_t vstride = (height + mStrideAlign - 1) & ~(mStrideAlign - 1);
479 switch (layout.type) {
480 case C2PlanarLayout::TYPE_RGB:
481 case C2PlanarLayout::TYPE_RGBA: {
482 ConvertRGBToPlanarYUV(mConversionBuffer.data(), stride, vstride,
483 mConversionBuffer.size(), *rView.get());
484 vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, width, height,
485 mStrideAlign, mConversionBuffer.data());
486 break;
487 }
488 case C2PlanarLayout::TYPE_YUV: {
489 if (!IsYUV420(*rView)) {
490 ALOGE("input is not YUV420");
491 work->result = C2_BAD_VALUE;
492 return;
493 }
494
495 if (layout.planes[layout.PLANE_Y].colInc == 1
496 && layout.planes[layout.PLANE_U].colInc == 1
497 && layout.planes[layout.PLANE_V].colInc == 1) {
498 // I420 compatible - though with custom offset and stride
499 vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, width, height,
500 mStrideAlign, (uint8_t*)rView->data()[0]);
501 raw_frame.planes[1] = (uint8_t*)rView->data()[1];
502 raw_frame.planes[2] = (uint8_t*)rView->data()[2];
503 raw_frame.stride[0] = layout.planes[layout.PLANE_Y].rowInc;
504 raw_frame.stride[1] = layout.planes[layout.PLANE_U].rowInc;
505 raw_frame.stride[2] = layout.planes[layout.PLANE_V].rowInc;
506 } else {
507 // copy to I420
508 MediaImage2 img = CreateYUV420PlanarMediaImage2(width, height, stride, vstride);
509 if (mConversionBuffer.size() >= stride * vstride * 3 / 2) {
510 status_t err = ImageCopy(mConversionBuffer.data(), &img, *rView);
511 if (err != OK) {
512 ALOGE("Buffer conversion failed: %d", err);
513 work->result = C2_BAD_VALUE;
514 return;
515 }
516 vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, stride, vstride,
517 mStrideAlign, (uint8_t*)rView->data()[0]);
518 vpx_img_set_rect(&raw_frame, 0, 0, width, height);
519 } else {
520 ALOGE("Conversion buffer is too small: %u x %u for %zu",
521 stride, vstride, mConversionBuffer.size());
522 work->result = C2_BAD_VALUE;
523 return;
524 }
525 }
526 break;
527 }
528 default:
529 ALOGE("Unrecognized plane type: %d", layout.type);
530 work->result = C2_BAD_VALUE;
531 return;
532 }
533
534 vpx_enc_frame_flags_t flags = getEncodeFlags();
535 // handle dynamic config parameters
536 {
537 IntfImpl::Lock lock = mIntf->lock();
538 std::shared_ptr<C2StreamIntraRefreshTuning::output> intraRefresh = mIntf->getIntraRefresh_l();
539 std::shared_ptr<C2StreamBitrateInfo::output> bitrate = mIntf->getBitrate_l();
540 std::shared_ptr<C2StreamRequestSyncFrameTuning::output> requestSync = mIntf->getRequestSync_l();
541 lock.unlock();
542
543 if (intraRefresh != mIntraRefresh) {
544 mIntraRefresh = intraRefresh;
545 ALOGV("Got mIntraRefresh request");
546 }
547
548 if (requestSync != mRequestSync) {
549 // we can handle IDR immediately
550 if (requestSync->value) {
551 // unset request
552 C2StreamRequestSyncFrameTuning::output clearSync(0u, C2_FALSE);
553 std::vector<std::unique_ptr<C2SettingResult>> failures;
554 mIntf->config({ &clearSync }, C2_MAY_BLOCK, &failures);
555 ALOGV("Got sync request");
556 flags |= VPX_EFLAG_FORCE_KF;
557 }
558 mRequestSync = requestSync;
559 }
560
561 if (bitrate != mBitrate) {
562 mBitrate = bitrate;
563 mCodecConfiguration->rc_target_bitrate =
564 (mBitrate->value + 500) / 1000;
565 vpx_codec_err_t res = vpx_codec_enc_config_set(mCodecContext,
566 mCodecConfiguration);
567 if (res != VPX_CODEC_OK) {
568 ALOGE("vpx encoder failed to update bitrate: %s",
569 vpx_codec_err_to_string(res));
570 mSignalledError = true;
571 work->result = C2_CORRUPTED;
572 return;
573 }
574 }
575 }
576
577 uint64_t inputTimeStamp = work->input.ordinal.timestamp.peekull();
578 uint32_t frameDuration;
579 if (inputTimeStamp > mLastTimestamp) {
580 frameDuration = (uint32_t)(inputTimeStamp - mLastTimestamp);
581 } else {
582 // Use default of 30 fps in case of 0 frame rate.
583 float frameRate = mFrameRate->value;
584 if (frameRate < 0.001) {
585 frameRate = 30;
586 }
587 frameDuration = (uint32_t)(1000000 / frameRate + 0.5);
588 }
589 mLastTimestamp = inputTimeStamp;
590
591 vpx_codec_err_t codec_return = vpx_codec_encode(mCodecContext, &raw_frame,
592 inputTimeStamp,
593 frameDuration, flags,
594 VPX_DL_REALTIME);
595 if (codec_return != VPX_CODEC_OK) {
596 ALOGE("vpx encoder failed to encode frame");
597 mSignalledError = true;
598 work->result = C2_CORRUPTED;
599 return;
600 }
601
602 bool populated = false;
603 vpx_codec_iter_t encoded_packet_iterator = nullptr;
604 const vpx_codec_cx_pkt_t* encoded_packet;
605 while ((encoded_packet = vpx_codec_get_cx_data(
606 mCodecContext, &encoded_packet_iterator))) {
607 if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) {
608 std::shared_ptr<C2LinearBlock> block;
609 C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
610 c2_status_t err = pool->fetchLinearBlock(encoded_packet->data.frame.sz, usage, &block);
611 if (err != C2_OK) {
612 ALOGE("fetchLinearBlock for Output failed with status %d", err);
613 work->result = C2_NO_MEMORY;
614 return;
615 }
616 C2WriteView wView = block->map().get();
617 if (wView.error()) {
618 ALOGE("write view map failed %d", wView.error());
619 work->result = C2_CORRUPTED;
620 return;
621 }
622
623 memcpy(wView.data(), encoded_packet->data.frame.buf, encoded_packet->data.frame.sz);
624 ++mNumInputFrames;
625
626 ALOGD("bytes generated %zu", encoded_packet->data.frame.sz);
627 uint32_t flags = 0;
628 if (eos) {
629 flags |= C2FrameData::FLAG_END_OF_STREAM;
630 }
631 work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
632 work->worklets.front()->output.buffers.clear();
633 std::shared_ptr<C2Buffer> buffer = createLinearBuffer(block);
634 if (encoded_packet->data.frame.flags & VPX_FRAME_IS_KEY) {
635 buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
636 0u /* stream id */, C2PictureTypeKeyFrame));
637 }
638 work->worklets.front()->output.buffers.push_back(buffer);
639 work->worklets.front()->output.ordinal = work->input.ordinal;
640 work->worklets.front()->output.ordinal.timestamp = encoded_packet->data.frame.pts;
641 work->workletsProcessed = 1u;
642 populated = true;
643 if (eos) {
644 mSignalledOutputEos = true;
645 ALOGV("signalled EOS");
646 }
647 }
648 }
649 if (!populated) {
650 work->workletsProcessed = 0u;
651 }
652 }
653
drain(uint32_t drainMode,const std::shared_ptr<C2BlockPool> & pool)654 c2_status_t C2SoftVpxEnc::drain(
655 uint32_t drainMode,
656 const std::shared_ptr<C2BlockPool> &pool) {
657 (void)pool;
658 if (drainMode == NO_DRAIN) {
659 ALOGW("drain with NO_DRAIN: no-op");
660 return C2_OK;
661 }
662 if (drainMode == DRAIN_CHAIN) {
663 ALOGW("DRAIN_CHAIN not supported");
664 return C2_OMITTED;
665 }
666
667 return C2_OK;
668 }
669
670 } // namespace android
671