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