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