SoftVPXEncoder.cpp revision a0a63e13788a77bc502da0c72269d82c4779ac91
1/*
2 * Copyright (C) 2013 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 "SoftVPXEncoder"
19#include "SoftVPXEncoder.h"
20
21#include <utils/Log.h>
22
23#include <media/hardware/HardwareAPI.h>
24#include <media/hardware/MetadataBufferType.h>
25#include <media/stagefright/foundation/ADebug.h>
26#include <media/stagefright/MediaDefs.h>
27
28namespace android {
29
30
31template<class T>
32static void InitOMXParams(T *params) {
33    params->nSize = sizeof(T);
34    // OMX IL 1.1.2
35    params->nVersion.s.nVersionMajor = 1;
36    params->nVersion.s.nVersionMinor = 1;
37    params->nVersion.s.nRevision = 2;
38    params->nVersion.s.nStep = 0;
39}
40
41
42static int GetCPUCoreCount() {
43    int cpuCoreCount = 1;
44#if defined(_SC_NPROCESSORS_ONLN)
45    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
46#else
47    // _SC_NPROC_ONLN must be defined...
48    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
49#endif
50    CHECK_GE(cpuCoreCount, 1);
51    return cpuCoreCount;
52}
53
54
55// This color conversion utility is copied from SoftMPEG4Encoder.cpp
56inline static void ConvertSemiPlanarToPlanar(uint8_t *inyuv,
57                                             uint8_t* outyuv,
58                                             int32_t width,
59                                             int32_t height) {
60    int32_t outYsize = width * height;
61    uint32_t *outy =  (uint32_t *) outyuv;
62    uint16_t *outcb = (uint16_t *) (outyuv + outYsize);
63    uint16_t *outcr = (uint16_t *) (outyuv + outYsize + (outYsize >> 2));
64
65    /* Y copying */
66    memcpy(outy, inyuv, outYsize);
67
68    /* U & V copying */
69    uint32_t *inyuv_4 = (uint32_t *) (inyuv + outYsize);
70    for (int32_t i = height >> 1; i > 0; --i) {
71        for (int32_t j = width >> 2; j > 0; --j) {
72            uint32_t temp = *inyuv_4++;
73            uint32_t tempU = temp & 0xFF;
74            tempU = tempU | ((temp >> 8) & 0xFF00);
75
76            uint32_t tempV = (temp >> 8) & 0xFF;
77            tempV = tempV | ((temp >> 16) & 0xFF00);
78
79            // Flip U and V
80            *outcb++ = tempV;
81            *outcr++ = tempU;
82        }
83    }
84}
85
86static void ConvertRGB32ToPlanar(
87        const uint8_t *src, uint8_t *dstY, int32_t width, int32_t height) {
88    CHECK((width & 1) == 0);
89    CHECK((height & 1) == 0);
90
91    uint8_t *dstU = dstY + width * height;
92    uint8_t *dstV = dstU + (width / 2) * (height / 2);
93
94    for (int32_t y = 0; y < height; ++y) {
95        for (int32_t x = 0; x < width; ++x) {
96#ifdef SURFACE_IS_BGR32
97            unsigned blue = src[4 * x];
98            unsigned green = src[4 * x + 1];
99            unsigned red= src[4 * x + 2];
100#else
101            unsigned red= src[4 * x];
102            unsigned green = src[4 * x + 1];
103            unsigned blue = src[4 * x + 2];
104#endif
105
106            unsigned luma =
107                ((red * 66 + green * 129 + blue * 25) >> 8) + 16;
108
109            dstY[x] = luma;
110
111            if ((x & 1) == 0 && (y & 1) == 0) {
112                unsigned U =
113                    ((-red * 38 - green * 74 + blue * 112) >> 8) + 128;
114
115                unsigned V =
116                    ((red * 112 - green * 94 - blue * 18) >> 8) + 128;
117
118                dstU[x / 2] = U;
119                dstV[x / 2] = V;
120            }
121        }
122
123        if ((y & 1) == 0) {
124            dstU += width / 2;
125            dstV += width / 2;
126        }
127
128        src += 4 * width;
129        dstY += width;
130    }
131}
132
133SoftVPXEncoder::SoftVPXEncoder(const char *name,
134                               const OMX_CALLBACKTYPE *callbacks,
135                               OMX_PTR appData,
136                               OMX_COMPONENTTYPE **component)
137    : SimpleSoftOMXComponent(name, callbacks, appData, component),
138      mCodecContext(NULL),
139      mCodecConfiguration(NULL),
140      mCodecInterface(NULL),
141      mWidth(176),
142      mHeight(144),
143      mBitrate(192000),  // in bps
144      mBitrateControlMode(VPX_VBR),  // variable bitrate
145      mFrameDurationUs(33333),  // Defaults to 30 fps
146      mDCTPartitions(0),
147      mErrorResilience(OMX_FALSE),
148      mColorFormat(OMX_COLOR_FormatYUV420Planar),
149      mLevel(OMX_VIDEO_VP8Level_Version0),
150      mConversionBuffer(NULL),
151      mInputDataIsMeta(false),
152      mGrallocModule(NULL) {
153    initPorts();
154}
155
156
157SoftVPXEncoder::~SoftVPXEncoder() {
158    releaseEncoder();
159}
160
161
162void SoftVPXEncoder::initPorts() {
163    OMX_PARAM_PORTDEFINITIONTYPE inputPort;
164    OMX_PARAM_PORTDEFINITIONTYPE outputPort;
165
166    InitOMXParams(&inputPort);
167    InitOMXParams(&outputPort);
168
169    inputPort.nBufferCountMin = kNumBuffers;
170    inputPort.nBufferCountActual = inputPort.nBufferCountMin;
171    inputPort.bEnabled = OMX_TRUE;
172    inputPort.bPopulated = OMX_FALSE;
173    inputPort.eDomain = OMX_PortDomainVideo;
174    inputPort.bBuffersContiguous = OMX_FALSE;
175    inputPort.format.video.pNativeRender = NULL;
176    inputPort.format.video.nFrameWidth = mWidth;
177    inputPort.format.video.nFrameHeight = mHeight;
178    inputPort.format.video.nStride = inputPort.format.video.nFrameWidth;
179    inputPort.format.video.nSliceHeight = inputPort.format.video.nFrameHeight;
180    inputPort.format.video.nBitrate = 0;
181    // frameRate is reciprocal of frameDuration, which is
182    // in microseconds. It is also in Q16 format.
183    inputPort.format.video.xFramerate = (1000000/mFrameDurationUs) << 16;
184    inputPort.format.video.bFlagErrorConcealment = OMX_FALSE;
185    inputPort.nPortIndex = kInputPortIndex;
186    inputPort.eDir = OMX_DirInput;
187    inputPort.nBufferAlignment = kInputBufferAlignment;
188    inputPort.format.video.cMIMEType =
189        const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW);
190    inputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
191    inputPort.format.video.eColorFormat = mColorFormat;
192    inputPort.format.video.pNativeWindow = NULL;
193    inputPort.nBufferSize =
194        (inputPort.format.video.nStride *
195        inputPort.format.video.nSliceHeight * 3) / 2;
196
197    addPort(inputPort);
198
199    outputPort.nBufferCountMin = kNumBuffers;
200    outputPort.nBufferCountActual = outputPort.nBufferCountMin;
201    outputPort.bEnabled = OMX_TRUE;
202    outputPort.bPopulated = OMX_FALSE;
203    outputPort.eDomain = OMX_PortDomainVideo;
204    outputPort.bBuffersContiguous = OMX_FALSE;
205    outputPort.format.video.pNativeRender = NULL;
206    outputPort.format.video.nFrameWidth = mWidth;
207    outputPort.format.video.nFrameHeight = mHeight;
208    outputPort.format.video.nStride = outputPort.format.video.nFrameWidth;
209    outputPort.format.video.nSliceHeight = outputPort.format.video.nFrameHeight;
210    outputPort.format.video.nBitrate = mBitrate;
211    outputPort.format.video.xFramerate = 0;
212    outputPort.format.video.bFlagErrorConcealment = OMX_FALSE;
213    outputPort.nPortIndex = kOutputPortIndex;
214    outputPort.eDir = OMX_DirOutput;
215    outputPort.nBufferAlignment = kOutputBufferAlignment;
216    outputPort.format.video.cMIMEType =
217        const_cast<char *>(MEDIA_MIMETYPE_VIDEO_VP8);
218    outputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingVP8;
219    outputPort.format.video.eColorFormat = OMX_COLOR_FormatUnused;
220    outputPort.format.video.pNativeWindow = NULL;
221    outputPort.nBufferSize = 256 * 1024;  // arbitrary
222
223    addPort(outputPort);
224}
225
226
227status_t SoftVPXEncoder::initEncoder() {
228    vpx_codec_err_t codec_return;
229
230    mCodecContext = new vpx_codec_ctx_t;
231    mCodecConfiguration = new vpx_codec_enc_cfg_t;
232    mCodecInterface = vpx_codec_vp8_cx();
233
234    if (mCodecInterface == NULL) {
235        return UNKNOWN_ERROR;
236    }
237
238    codec_return = vpx_codec_enc_config_default(mCodecInterface,
239                                                mCodecConfiguration,
240                                                0);  // Codec specific flags
241
242    if (codec_return != VPX_CODEC_OK) {
243        ALOGE("Error populating default configuration for vpx encoder.");
244        return UNKNOWN_ERROR;
245    }
246
247    mCodecConfiguration->g_w = mWidth;
248    mCodecConfiguration->g_h = mHeight;
249    mCodecConfiguration->g_threads = GetCPUCoreCount();
250    mCodecConfiguration->g_error_resilient = mErrorResilience;
251
252    switch (mLevel) {
253        case OMX_VIDEO_VP8Level_Version0:
254            mCodecConfiguration->g_profile = 0;
255            break;
256
257        case OMX_VIDEO_VP8Level_Version1:
258            mCodecConfiguration->g_profile = 1;
259            break;
260
261        case OMX_VIDEO_VP8Level_Version2:
262            mCodecConfiguration->g_profile = 2;
263            break;
264
265        case OMX_VIDEO_VP8Level_Version3:
266            mCodecConfiguration->g_profile = 3;
267            break;
268
269        default:
270            mCodecConfiguration->g_profile = 0;
271    }
272
273    // OMX timebase unit is microsecond
274    // g_timebase is in seconds (i.e. 1/1000000 seconds)
275    mCodecConfiguration->g_timebase.num = 1;
276    mCodecConfiguration->g_timebase.den = 1000000;
277    // rc_target_bitrate is in kbps, mBitrate in bps
278    mCodecConfiguration->rc_target_bitrate = mBitrate/1000;
279    mCodecConfiguration->rc_end_usage = mBitrateControlMode;
280
281    codec_return = vpx_codec_enc_init(mCodecContext,
282                                      mCodecInterface,
283                                      mCodecConfiguration,
284                                      0);  // flags
285
286    if (codec_return != VPX_CODEC_OK) {
287        ALOGE("Error initializing vpx encoder");
288        return UNKNOWN_ERROR;
289    }
290
291    codec_return = vpx_codec_control(mCodecContext,
292                                     VP8E_SET_TOKEN_PARTITIONS,
293                                     mDCTPartitions);
294    if (codec_return != VPX_CODEC_OK) {
295        ALOGE("Error setting dct partitions for vpx encoder.");
296        return UNKNOWN_ERROR;
297    }
298
299    if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || mInputDataIsMeta) {
300        if (mConversionBuffer == NULL) {
301            mConversionBuffer = (uint8_t *)malloc(mWidth * mHeight * 3 / 2);
302            if (mConversionBuffer == NULL) {
303                ALOGE("Allocating conversion buffer failed.");
304                return UNKNOWN_ERROR;
305            }
306        }
307    }
308    return OK;
309}
310
311
312status_t SoftVPXEncoder::releaseEncoder() {
313    if (mCodecContext != NULL) {
314        vpx_codec_destroy(mCodecContext);
315        delete mCodecContext;
316        mCodecContext = NULL;
317    }
318
319    if (mCodecConfiguration != NULL) {
320        delete mCodecConfiguration;
321        mCodecConfiguration = NULL;
322    }
323
324    if (mConversionBuffer != NULL) {
325        delete mConversionBuffer;
326        mConversionBuffer = NULL;
327    }
328
329    // this one is not allocated by us
330    mCodecInterface = NULL;
331
332    return OK;
333}
334
335
336OMX_ERRORTYPE SoftVPXEncoder::internalGetParameter(OMX_INDEXTYPE index,
337                                                   OMX_PTR param) {
338    // can include extension index OMX_INDEXEXTTYPE
339    const int32_t indexFull = index;
340
341    switch (indexFull) {
342        case OMX_IndexParamVideoPortFormat: {
343            OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
344                (OMX_VIDEO_PARAM_PORTFORMATTYPE *)param;
345
346            if (formatParams->nPortIndex == kInputPortIndex) {
347                if (formatParams->nIndex >= kNumberOfSupportedColorFormats) {
348                    return OMX_ErrorNoMore;
349                }
350
351                // Color formats, in order of preference
352                if (formatParams->nIndex == 0) {
353                    formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
354                } else if (formatParams->nIndex == 1) {
355                    formatParams->eColorFormat =
356                        OMX_COLOR_FormatYUV420SemiPlanar;
357                } else {
358                    formatParams->eColorFormat = OMX_COLOR_FormatAndroidOpaque;
359                }
360
361                formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
362                // Converting from microseconds
363                // Also converting to Q16 format
364                formatParams->xFramerate = (1000000/mFrameDurationUs) << 16;
365                return OMX_ErrorNone;
366            } else if (formatParams->nPortIndex == kOutputPortIndex) {
367                formatParams->eCompressionFormat = OMX_VIDEO_CodingVP8;
368                formatParams->eColorFormat = OMX_COLOR_FormatUnused;
369                formatParams->xFramerate = 0;
370                return OMX_ErrorNone;
371            } else {
372                return OMX_ErrorBadPortIndex;
373            }
374        }
375
376        case OMX_IndexParamVideoBitrate: {
377            OMX_VIDEO_PARAM_BITRATETYPE *bitrate =
378                (OMX_VIDEO_PARAM_BITRATETYPE *)param;
379
380                if (bitrate->nPortIndex != kOutputPortIndex) {
381                    return OMX_ErrorUnsupportedIndex;
382                }
383
384                bitrate->nTargetBitrate = mBitrate;
385
386                if (mBitrateControlMode == VPX_VBR) {
387                    bitrate->eControlRate = OMX_Video_ControlRateVariable;
388                } else if (mBitrateControlMode == VPX_CBR) {
389                    bitrate->eControlRate = OMX_Video_ControlRateConstant;
390                } else {
391                    return OMX_ErrorUnsupportedSetting;
392                }
393                return OMX_ErrorNone;
394        }
395
396        // VP8 specific parameters that use extension headers
397        case OMX_IndexParamVideoVp8: {
398            OMX_VIDEO_PARAM_VP8TYPE *vp8Params =
399                (OMX_VIDEO_PARAM_VP8TYPE *)param;
400
401                if (vp8Params->nPortIndex != kOutputPortIndex) {
402                    return OMX_ErrorUnsupportedIndex;
403                }
404
405                vp8Params->eProfile = OMX_VIDEO_VP8ProfileMain;
406                vp8Params->eLevel = mLevel;
407                vp8Params->nDCTPartitions = mDCTPartitions;
408                vp8Params->bErrorResilientMode = mErrorResilience;
409                return OMX_ErrorNone;
410        }
411
412        case OMX_IndexParamVideoProfileLevelQuerySupported: {
413            OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileAndLevel =
414                (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param;
415
416            if (profileAndLevel->nPortIndex != kOutputPortIndex) {
417                return OMX_ErrorUnsupportedIndex;
418            }
419
420            switch (profileAndLevel->nProfileIndex) {
421                case 0:
422                    profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version0;
423                    break;
424
425                case 1:
426                    profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version1;
427                    break;
428
429                case 2:
430                    profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version2;
431                    break;
432
433                case 3:
434                    profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version3;
435                    break;
436
437                default:
438                    return OMX_ErrorNoMore;
439            }
440
441            profileAndLevel->eProfile = OMX_VIDEO_VP8ProfileMain;
442            return OMX_ErrorNone;
443        }
444
445        case OMX_IndexParamVideoProfileLevelCurrent: {
446            OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileAndLevel =
447                (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param;
448
449            if (profileAndLevel->nPortIndex != kOutputPortIndex) {
450                return OMX_ErrorUnsupportedIndex;
451            }
452
453            profileAndLevel->eLevel = mLevel;
454            profileAndLevel->eProfile = OMX_VIDEO_VP8ProfileMain;
455            return OMX_ErrorNone;
456        }
457
458        default:
459            return SimpleSoftOMXComponent::internalGetParameter(index, param);
460    }
461}
462
463
464OMX_ERRORTYPE SoftVPXEncoder::internalSetParameter(OMX_INDEXTYPE index,
465                                                   const OMX_PTR param) {
466    // can include extension index OMX_INDEXEXTTYPE
467    const int32_t indexFull = index;
468
469    switch (indexFull) {
470        case OMX_IndexParamStandardComponentRole:
471            return internalSetRoleParams(
472                (const OMX_PARAM_COMPONENTROLETYPE *)param);
473
474        case OMX_IndexParamVideoBitrate:
475            return internalSetBitrateParams(
476                (const OMX_VIDEO_PARAM_BITRATETYPE *)param);
477
478        case OMX_IndexParamPortDefinition:
479        {
480            OMX_ERRORTYPE err = internalSetPortParams(
481                (const OMX_PARAM_PORTDEFINITIONTYPE *)param);
482
483            if (err != OMX_ErrorNone) {
484                return err;
485            }
486
487            return SimpleSoftOMXComponent::internalSetParameter(index, param);
488        }
489
490        case OMX_IndexParamVideoPortFormat:
491            return internalSetFormatParams(
492                (const OMX_VIDEO_PARAM_PORTFORMATTYPE *)param);
493
494        case OMX_IndexParamVideoVp8:
495            return internalSetVp8Params(
496                (const OMX_VIDEO_PARAM_VP8TYPE *)param);
497
498        case OMX_IndexParamVideoProfileLevelCurrent:
499            return internalSetProfileLevel(
500                (const OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param);
501
502        case OMX_IndexVendorStartUnused:
503        {
504            // storeMetaDataInBuffers
505            const StoreMetaDataInBuffersParams *storeParam =
506                (const StoreMetaDataInBuffersParams *)param;
507
508            if (storeParam->nPortIndex != kInputPortIndex) {
509                return OMX_ErrorBadPortIndex;
510            }
511
512            mInputDataIsMeta = (storeParam->bStoreMetaData == OMX_TRUE);
513
514            return OMX_ErrorNone;
515        }
516
517        default:
518            return SimpleSoftOMXComponent::internalSetParameter(index, param);
519    }
520}
521
522OMX_ERRORTYPE SoftVPXEncoder::internalSetProfileLevel(
523        const OMX_VIDEO_PARAM_PROFILELEVELTYPE* profileAndLevel) {
524    if (profileAndLevel->nPortIndex != kOutputPortIndex) {
525        return OMX_ErrorUnsupportedIndex;
526    }
527
528    if (profileAndLevel->eProfile != OMX_VIDEO_VP8ProfileMain) {
529        return OMX_ErrorBadParameter;
530    }
531
532    if (profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version0 ||
533        profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version1 ||
534        profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version2 ||
535        profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version3) {
536        mLevel = (OMX_VIDEO_VP8LEVELTYPE)profileAndLevel->eLevel;
537    } else {
538        return OMX_ErrorBadParameter;
539    }
540
541    return OMX_ErrorNone;
542}
543
544
545OMX_ERRORTYPE SoftVPXEncoder::internalSetVp8Params(
546        const OMX_VIDEO_PARAM_VP8TYPE* vp8Params) {
547    if (vp8Params->nPortIndex != kOutputPortIndex) {
548        return OMX_ErrorUnsupportedIndex;
549    }
550
551    if (vp8Params->eProfile != OMX_VIDEO_VP8ProfileMain) {
552        return OMX_ErrorBadParameter;
553    }
554
555    if (vp8Params->eLevel == OMX_VIDEO_VP8Level_Version0 ||
556        vp8Params->eLevel == OMX_VIDEO_VP8Level_Version1 ||
557        vp8Params->eLevel == OMX_VIDEO_VP8Level_Version2 ||
558        vp8Params->eLevel == OMX_VIDEO_VP8Level_Version3) {
559        mLevel = vp8Params->eLevel;
560    } else {
561        return OMX_ErrorBadParameter;
562    }
563
564    if (vp8Params->nDCTPartitions <= kMaxDCTPartitions) {
565        mDCTPartitions = vp8Params->nDCTPartitions;
566    } else {
567        return OMX_ErrorBadParameter;
568    }
569
570    mErrorResilience = vp8Params->bErrorResilientMode;
571    return OMX_ErrorNone;
572}
573
574
575OMX_ERRORTYPE SoftVPXEncoder::internalSetFormatParams(
576        const OMX_VIDEO_PARAM_PORTFORMATTYPE* format) {
577    if (format->nPortIndex == kInputPortIndex) {
578        if (format->eColorFormat == OMX_COLOR_FormatYUV420Planar ||
579            format->eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar ||
580            format->eColorFormat == OMX_COLOR_FormatAndroidOpaque) {
581            mColorFormat = format->eColorFormat;
582
583            OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kInputPortIndex)->mDef;
584            def->format.video.eColorFormat = mColorFormat;
585
586            return OMX_ErrorNone;
587        } else {
588            ALOGE("Unsupported color format %i", format->eColorFormat);
589            return OMX_ErrorUnsupportedSetting;
590        }
591    } else if (format->nPortIndex == kOutputPortIndex) {
592        if (format->eCompressionFormat == OMX_VIDEO_CodingVP8) {
593            return OMX_ErrorNone;
594        } else {
595            return OMX_ErrorUnsupportedSetting;
596        }
597    } else {
598        return OMX_ErrorBadPortIndex;
599    }
600}
601
602
603OMX_ERRORTYPE SoftVPXEncoder::internalSetRoleParams(
604        const OMX_PARAM_COMPONENTROLETYPE* role) {
605    const char* roleText = (const char*)role->cRole;
606    const size_t roleTextMaxSize = OMX_MAX_STRINGNAME_SIZE - 1;
607
608    if (strncmp(roleText, "video_encoder.vp8", roleTextMaxSize)) {
609        ALOGE("Unsupported component role");
610        return OMX_ErrorBadParameter;
611    }
612
613    return OMX_ErrorNone;
614}
615
616
617OMX_ERRORTYPE SoftVPXEncoder::internalSetPortParams(
618        const OMX_PARAM_PORTDEFINITIONTYPE* port) {
619    if (port->nPortIndex == kInputPortIndex) {
620        mWidth = port->format.video.nFrameWidth;
621        mHeight = port->format.video.nFrameHeight;
622
623        // xFramerate comes in Q16 format, in frames per second unit
624        const uint32_t framerate = port->format.video.xFramerate >> 16;
625        // frame duration is in microseconds
626        mFrameDurationUs = (1000000/framerate);
627
628        if (port->format.video.eColorFormat == OMX_COLOR_FormatYUV420Planar ||
629            port->format.video.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar ||
630            port->format.video.eColorFormat == OMX_COLOR_FormatAndroidOpaque) {
631            mColorFormat = port->format.video.eColorFormat;
632        } else {
633            return OMX_ErrorUnsupportedSetting;
634        }
635
636        OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kInputPortIndex)->mDef;
637        def->format.video.nFrameWidth = mWidth;
638        def->format.video.nFrameHeight = mHeight;
639        def->format.video.xFramerate = port->format.video.xFramerate;
640        def->format.video.eColorFormat = mColorFormat;
641
642        return OMX_ErrorNone;
643    } else if (port->nPortIndex == kOutputPortIndex) {
644        mBitrate = port->format.video.nBitrate;
645        return OMX_ErrorNone;
646    } else {
647        return OMX_ErrorBadPortIndex;
648    }
649}
650
651
652OMX_ERRORTYPE SoftVPXEncoder::internalSetBitrateParams(
653        const OMX_VIDEO_PARAM_BITRATETYPE* bitrate) {
654    if (bitrate->nPortIndex != kOutputPortIndex) {
655        return OMX_ErrorUnsupportedIndex;
656    }
657
658    mBitrate = bitrate->nTargetBitrate;
659
660    if (bitrate->eControlRate == OMX_Video_ControlRateVariable) {
661        mBitrateControlMode = VPX_VBR;
662    } else if (bitrate->eControlRate == OMX_Video_ControlRateConstant) {
663        mBitrateControlMode = VPX_CBR;
664    } else {
665        return OMX_ErrorUnsupportedSetting;
666    }
667
668    return OMX_ErrorNone;
669}
670
671
672void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) {
673    // Initialize encoder if not already
674    if (mCodecContext == NULL) {
675        if (OK != initEncoder()) {
676            ALOGE("Failed to initialize encoder");
677            notify(OMX_EventError,
678                   OMX_ErrorUndefined,
679                   0,  // Extra notification data
680                   NULL);  // Notification data pointer
681            return;
682        }
683    }
684
685    vpx_codec_err_t codec_return;
686    List<BufferInfo *> &inputBufferInfoQueue = getPortQueue(kInputPortIndex);
687    List<BufferInfo *> &outputBufferInfoQueue = getPortQueue(kOutputPortIndex);
688
689    while (!inputBufferInfoQueue.empty() && !outputBufferInfoQueue.empty()) {
690        BufferInfo *inputBufferInfo = *inputBufferInfoQueue.begin();
691        OMX_BUFFERHEADERTYPE *inputBufferHeader = inputBufferInfo->mHeader;
692
693        BufferInfo *outputBufferInfo = *outputBufferInfoQueue.begin();
694        OMX_BUFFERHEADERTYPE *outputBufferHeader = outputBufferInfo->mHeader;
695
696        if (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) {
697            inputBufferInfoQueue.erase(inputBufferInfoQueue.begin());
698            inputBufferInfo->mOwnedByUs = false;
699            notifyEmptyBufferDone(inputBufferHeader);
700
701            outputBufferHeader->nFilledLen = 0;
702            outputBufferHeader->nFlags = OMX_BUFFERFLAG_EOS;
703
704            outputBufferInfoQueue.erase(outputBufferInfoQueue.begin());
705            outputBufferInfo->mOwnedByUs = false;
706            notifyFillBufferDone(outputBufferHeader);
707            return;
708        }
709
710        uint8_t *source =
711            inputBufferHeader->pBuffer + inputBufferHeader->nOffset;
712
713        if (mInputDataIsMeta) {
714            CHECK_GE(inputBufferHeader->nFilledLen,
715                     4 + sizeof(buffer_handle_t));
716
717            uint32_t bufferType = *(uint32_t *)source;
718            CHECK_EQ(bufferType, kMetadataBufferTypeGrallocSource);
719
720            if (mGrallocModule == NULL) {
721                CHECK_EQ(0, hw_get_module(
722                            GRALLOC_HARDWARE_MODULE_ID, &mGrallocModule));
723            }
724
725            const gralloc_module_t *grmodule =
726                (const gralloc_module_t *)mGrallocModule;
727
728            buffer_handle_t handle = *(buffer_handle_t *)(source + 4);
729
730            void *bits;
731            CHECK_EQ(0,
732                     grmodule->lock(
733                         grmodule, handle,
734                         GRALLOC_USAGE_SW_READ_OFTEN
735                            | GRALLOC_USAGE_SW_WRITE_NEVER,
736                         0, 0, mWidth, mHeight, &bits));
737
738            ConvertRGB32ToPlanar(
739                    (const uint8_t *)bits, mConversionBuffer, mWidth, mHeight);
740
741            source = mConversionBuffer;
742
743            CHECK_EQ(0, grmodule->unlock(grmodule, handle));
744        } else if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
745            ConvertSemiPlanarToPlanar(
746                    source, mConversionBuffer, mWidth, mHeight);
747
748            source = mConversionBuffer;
749        }
750        vpx_image_t raw_frame;
751        vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, mWidth, mHeight,
752                     kInputBufferAlignment, source);
753        codec_return = vpx_codec_encode(
754                mCodecContext,
755                &raw_frame,
756                inputBufferHeader->nTimeStamp,  // in timebase units
757                mFrameDurationUs,  // frame duration in timebase units
758                0,  // frame flags
759                VPX_DL_REALTIME);  // encoding deadline
760        if (codec_return != VPX_CODEC_OK) {
761            ALOGE("vpx encoder failed to encode frame");
762            notify(OMX_EventError,
763                   OMX_ErrorUndefined,
764                   0,  // Extra notification data
765                   NULL);  // Notification data pointer
766            return;
767        }
768
769        vpx_codec_iter_t encoded_packet_iterator = NULL;
770        const vpx_codec_cx_pkt_t* encoded_packet;
771
772        while ((encoded_packet = vpx_codec_get_cx_data(
773                        mCodecContext, &encoded_packet_iterator))) {
774            if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) {
775                outputBufferHeader->nTimeStamp = encoded_packet->data.frame.pts;
776                outputBufferHeader->nFlags = 0;
777                outputBufferHeader->nOffset = 0;
778                outputBufferHeader->nFilledLen = encoded_packet->data.frame.sz;
779                memcpy(outputBufferHeader->pBuffer,
780                       encoded_packet->data.frame.buf,
781                       encoded_packet->data.frame.sz);
782                outputBufferInfo->mOwnedByUs = false;
783                outputBufferInfoQueue.erase(outputBufferInfoQueue.begin());
784                notifyFillBufferDone(outputBufferHeader);
785            }
786        }
787
788        inputBufferInfo->mOwnedByUs = false;
789        inputBufferInfoQueue.erase(inputBufferInfoQueue.begin());
790        notifyEmptyBufferDone(inputBufferHeader);
791    }
792}
793
794OMX_ERRORTYPE SoftVPXEncoder::getExtensionIndex(
795        const char *name, OMX_INDEXTYPE *index) {
796    if (!strcmp(name, "OMX.google.android.index.storeMetaDataInBuffers")) {
797        *index = OMX_IndexVendorStartUnused;
798        return OMX_ErrorNone;
799    }
800
801    return SimpleSoftOMXComponent::getExtensionIndex(name, index);
802}
803
804}  // namespace android
805
806
807android::SoftOMXComponent *createSoftOMXComponent(
808        const char *name, const OMX_CALLBACKTYPE *callbacks,
809        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
810    return new android::SoftVPXEncoder(name, callbacks, appData, component);
811}
812