Home | History | Annotate | Download | only in h264dec
      1 /*
      2  * Copyright (C) 2011 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 "SoftAVC"
     19 #include <utils/Log.h>
     20 
     21 #include "SoftAVC.h"
     22 
     23 #include <media/stagefright/foundation/ADebug.h>
     24 #include <media/stagefright/MediaDefs.h>
     25 #include <media/stagefright/MediaErrors.h>
     26 #include <media/IOMX.h>
     27 
     28 
     29 namespace android {
     30 
     31 static const CodecProfileLevel kProfileLevels[] = {
     32     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1  },
     33     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1b },
     34     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel11 },
     35     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel12 },
     36     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel13 },
     37     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel2  },
     38     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel21 },
     39     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel22 },
     40     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel3  },
     41     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel31 },
     42     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel32 },
     43     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel4  },
     44     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 },
     45     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel42 },
     46     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel5  },
     47     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel51 },
     48 };
     49 
     50 template<class T>
     51 static void InitOMXParams(T *params) {
     52     params->nSize = sizeof(T);
     53     params->nVersion.s.nVersionMajor = 1;
     54     params->nVersion.s.nVersionMinor = 0;
     55     params->nVersion.s.nRevision = 0;
     56     params->nVersion.s.nStep = 0;
     57 }
     58 
     59 SoftAVC::SoftAVC(
     60         const char *name,
     61         const OMX_CALLBACKTYPE *callbacks,
     62         OMX_PTR appData,
     63         OMX_COMPONENTTYPE **component)
     64     : SimpleSoftOMXComponent(name, callbacks, appData, component),
     65       mHandle(NULL),
     66       mInputBufferCount(0),
     67       mWidth(320),
     68       mHeight(240),
     69       mPictureSize(mWidth * mHeight * 3 / 2),
     70       mCropLeft(0),
     71       mCropTop(0),
     72       mCropWidth(mWidth),
     73       mCropHeight(mHeight),
     74       mFirstPicture(NULL),
     75       mFirstPictureId(-1),
     76       mPicId(0),
     77       mHeadersDecoded(false),
     78       mEOSStatus(INPUT_DATA_AVAILABLE),
     79       mOutputPortSettingsChange(NONE),
     80       mSignalledError(false) {
     81     initPorts();
     82     CHECK_EQ(initDecoder(), (status_t)OK);
     83 }
     84 
     85 SoftAVC::~SoftAVC() {
     86     H264SwDecRelease(mHandle);
     87     mHandle = NULL;
     88 
     89     while (mPicToHeaderMap.size() != 0) {
     90         OMX_BUFFERHEADERTYPE *header = mPicToHeaderMap.editValueAt(0);
     91         mPicToHeaderMap.removeItemsAt(0);
     92         delete header;
     93         header = NULL;
     94     }
     95     List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
     96     List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex);
     97     CHECK(outQueue.empty());
     98     CHECK(inQueue.empty());
     99 
    100     delete[] mFirstPicture;
    101 }
    102 
    103 void SoftAVC::initPorts() {
    104     OMX_PARAM_PORTDEFINITIONTYPE def;
    105     InitOMXParams(&def);
    106 
    107     def.nPortIndex = kInputPortIndex;
    108     def.eDir = OMX_DirInput;
    109     def.nBufferCountMin = kNumInputBuffers;
    110     def.nBufferCountActual = def.nBufferCountMin;
    111     def.nBufferSize = 8192;
    112     def.bEnabled = OMX_TRUE;
    113     def.bPopulated = OMX_FALSE;
    114     def.eDomain = OMX_PortDomainVideo;
    115     def.bBuffersContiguous = OMX_FALSE;
    116     def.nBufferAlignment = 1;
    117 
    118     def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_AVC);
    119     def.format.video.pNativeRender = NULL;
    120     def.format.video.nFrameWidth = mWidth;
    121     def.format.video.nFrameHeight = mHeight;
    122     def.format.video.nStride = def.format.video.nFrameWidth;
    123     def.format.video.nSliceHeight = def.format.video.nFrameHeight;
    124     def.format.video.nBitrate = 0;
    125     def.format.video.xFramerate = 0;
    126     def.format.video.bFlagErrorConcealment = OMX_FALSE;
    127     def.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC;
    128     def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
    129     def.format.video.pNativeWindow = NULL;
    130 
    131     addPort(def);
    132 
    133     def.nPortIndex = kOutputPortIndex;
    134     def.eDir = OMX_DirOutput;
    135     def.nBufferCountMin = kNumOutputBuffers;
    136     def.nBufferCountActual = def.nBufferCountMin;
    137     def.bEnabled = OMX_TRUE;
    138     def.bPopulated = OMX_FALSE;
    139     def.eDomain = OMX_PortDomainVideo;
    140     def.bBuffersContiguous = OMX_FALSE;
    141     def.nBufferAlignment = 2;
    142 
    143     def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW);
    144     def.format.video.pNativeRender = NULL;
    145     def.format.video.nFrameWidth = mWidth;
    146     def.format.video.nFrameHeight = mHeight;
    147     def.format.video.nStride = def.format.video.nFrameWidth;
    148     def.format.video.nSliceHeight = def.format.video.nFrameHeight;
    149     def.format.video.nBitrate = 0;
    150     def.format.video.xFramerate = 0;
    151     def.format.video.bFlagErrorConcealment = OMX_FALSE;
    152     def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
    153     def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
    154     def.format.video.pNativeWindow = NULL;
    155 
    156     def.nBufferSize =
    157         (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2;
    158 
    159     addPort(def);
    160 }
    161 
    162 status_t SoftAVC::initDecoder() {
    163     // Force decoder to output buffers in display order.
    164     if (H264SwDecInit(&mHandle, 0) == H264SWDEC_OK) {
    165         return OK;
    166     }
    167     return UNKNOWN_ERROR;
    168 }
    169 
    170 OMX_ERRORTYPE SoftAVC::internalGetParameter(
    171         OMX_INDEXTYPE index, OMX_PTR params) {
    172     switch (index) {
    173         case OMX_IndexParamVideoPortFormat:
    174         {
    175             OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
    176                 (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
    177 
    178             if (formatParams->nPortIndex > kOutputPortIndex) {
    179                 return OMX_ErrorUndefined;
    180             }
    181 
    182             if (formatParams->nIndex != 0) {
    183                 return OMX_ErrorNoMore;
    184             }
    185 
    186             if (formatParams->nPortIndex == kInputPortIndex) {
    187                 formatParams->eCompressionFormat = OMX_VIDEO_CodingAVC;
    188                 formatParams->eColorFormat = OMX_COLOR_FormatUnused;
    189                 formatParams->xFramerate = 0;
    190             } else {
    191                 CHECK(formatParams->nPortIndex == kOutputPortIndex);
    192 
    193                 formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
    194                 formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
    195                 formatParams->xFramerate = 0;
    196             }
    197 
    198             return OMX_ErrorNone;
    199         }
    200 
    201         case OMX_IndexParamVideoProfileLevelQuerySupported:
    202         {
    203             OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel =
    204                     (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) params;
    205 
    206             if (profileLevel->nPortIndex != kInputPortIndex) {
    207                 ALOGE("Invalid port index: %ld", profileLevel->nPortIndex);
    208                 return OMX_ErrorUnsupportedIndex;
    209             }
    210 
    211             size_t index = profileLevel->nProfileIndex;
    212             size_t nProfileLevels =
    213                     sizeof(kProfileLevels) / sizeof(kProfileLevels[0]);
    214             if (index >= nProfileLevels) {
    215                 return OMX_ErrorNoMore;
    216             }
    217 
    218             profileLevel->eProfile = kProfileLevels[index].mProfile;
    219             profileLevel->eLevel = kProfileLevels[index].mLevel;
    220             return OMX_ErrorNone;
    221         }
    222 
    223         default:
    224             return SimpleSoftOMXComponent::internalGetParameter(index, params);
    225     }
    226 }
    227 
    228 OMX_ERRORTYPE SoftAVC::internalSetParameter(
    229         OMX_INDEXTYPE index, const OMX_PTR params) {
    230     switch (index) {
    231         case OMX_IndexParamStandardComponentRole:
    232         {
    233             const OMX_PARAM_COMPONENTROLETYPE *roleParams =
    234                 (const OMX_PARAM_COMPONENTROLETYPE *)params;
    235 
    236             if (strncmp((const char *)roleParams->cRole,
    237                         "video_decoder.avc",
    238                         OMX_MAX_STRINGNAME_SIZE - 1)) {
    239                 return OMX_ErrorUndefined;
    240             }
    241 
    242             return OMX_ErrorNone;
    243         }
    244 
    245         case OMX_IndexParamVideoPortFormat:
    246         {
    247             OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
    248                 (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
    249 
    250             if (formatParams->nPortIndex > kOutputPortIndex) {
    251                 return OMX_ErrorUndefined;
    252             }
    253 
    254             if (formatParams->nIndex != 0) {
    255                 return OMX_ErrorNoMore;
    256             }
    257 
    258             return OMX_ErrorNone;
    259         }
    260 
    261         default:
    262             return SimpleSoftOMXComponent::internalSetParameter(index, params);
    263     }
    264 }
    265 
    266 OMX_ERRORTYPE SoftAVC::getConfig(
    267         OMX_INDEXTYPE index, OMX_PTR params) {
    268     switch (index) {
    269         case OMX_IndexConfigCommonOutputCrop:
    270         {
    271             OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params;
    272 
    273             if (rectParams->nPortIndex != 1) {
    274                 return OMX_ErrorUndefined;
    275             }
    276 
    277             rectParams->nLeft = mCropLeft;
    278             rectParams->nTop = mCropTop;
    279             rectParams->nWidth = mCropWidth;
    280             rectParams->nHeight = mCropHeight;
    281 
    282             return OMX_ErrorNone;
    283         }
    284 
    285         default:
    286             return OMX_ErrorUnsupportedIndex;
    287     }
    288 }
    289 
    290 void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
    291     if (mSignalledError || mOutputPortSettingsChange != NONE) {
    292         return;
    293     }
    294 
    295     if (mEOSStatus == OUTPUT_FRAMES_FLUSHED) {
    296         return;
    297     }
    298 
    299     List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex);
    300     List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
    301     H264SwDecRet ret = H264SWDEC_PIC_RDY;
    302     bool portSettingsChanged = false;
    303     while ((mEOSStatus != INPUT_DATA_AVAILABLE || !inQueue.empty())
    304             && outQueue.size() == kNumOutputBuffers) {
    305 
    306         if (mEOSStatus == INPUT_EOS_SEEN) {
    307             drainAllOutputBuffers();
    308             return;
    309         }
    310 
    311         BufferInfo *inInfo = *inQueue.begin();
    312         OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
    313         ++mPicId;
    314 
    315         OMX_BUFFERHEADERTYPE *header = new OMX_BUFFERHEADERTYPE;
    316         memset(header, 0, sizeof(OMX_BUFFERHEADERTYPE));
    317         header->nTimeStamp = inHeader->nTimeStamp;
    318         header->nFlags = inHeader->nFlags;
    319         if (header->nFlags & OMX_BUFFERFLAG_EOS) {
    320             mEOSStatus = INPUT_EOS_SEEN;
    321         }
    322         mPicToHeaderMap.add(mPicId, header);
    323         inQueue.erase(inQueue.begin());
    324 
    325         H264SwDecInput inPicture;
    326         H264SwDecOutput outPicture;
    327         memset(&inPicture, 0, sizeof(inPicture));
    328         inPicture.dataLen = inHeader->nFilledLen;
    329         inPicture.pStream = inHeader->pBuffer + inHeader->nOffset;
    330         inPicture.picId = mPicId;
    331         inPicture.intraConcealmentMethod = 1;
    332         H264SwDecPicture decodedPicture;
    333 
    334         while (inPicture.dataLen > 0) {
    335             ret = H264SwDecDecode(mHandle, &inPicture, &outPicture);
    336             if (ret == H264SWDEC_HDRS_RDY_BUFF_NOT_EMPTY ||
    337                 ret == H264SWDEC_PIC_RDY_BUFF_NOT_EMPTY) {
    338                 inPicture.dataLen -= (u32)(outPicture.pStrmCurrPos - inPicture.pStream);
    339                 inPicture.pStream = outPicture.pStrmCurrPos;
    340                 if (ret == H264SWDEC_HDRS_RDY_BUFF_NOT_EMPTY) {
    341                     mHeadersDecoded = true;
    342                     H264SwDecInfo decoderInfo;
    343                     CHECK(H264SwDecGetInfo(mHandle, &decoderInfo) == H264SWDEC_OK);
    344 
    345                     if (handlePortSettingChangeEvent(&decoderInfo)) {
    346                         portSettingsChanged = true;
    347                     }
    348 
    349                     if (decoderInfo.croppingFlag &&
    350                         handleCropRectEvent(&decoderInfo.cropParams)) {
    351                         portSettingsChanged = true;
    352                     }
    353                 }
    354             } else {
    355                 if (portSettingsChanged) {
    356                     if (H264SwDecNextPicture(mHandle, &decodedPicture, 0)
    357                         == H264SWDEC_PIC_RDY) {
    358 
    359                         // Save this output buffer; otherwise, it will be
    360                         // lost during dynamic port reconfiguration because
    361                         // OpenMAX client will delete _all_ output buffers
    362                         // in the process.
    363                         saveFirstOutputBuffer(
    364                             decodedPicture.picId,
    365                             (uint8_t *)decodedPicture.pOutputPicture);
    366                     }
    367                 }
    368                 inPicture.dataLen = 0;
    369                 if (ret < 0) {
    370                     ALOGE("Decoder failed: %d", ret);
    371 
    372                     notify(OMX_EventError, OMX_ErrorUndefined,
    373                            ERROR_MALFORMED, NULL);
    374 
    375                     mSignalledError = true;
    376                     return;
    377                 }
    378             }
    379         }
    380         inInfo->mOwnedByUs = false;
    381         notifyEmptyBufferDone(inHeader);
    382 
    383         if (portSettingsChanged) {
    384             portSettingsChanged = false;
    385             return;
    386         }
    387 
    388         if (mFirstPicture && !outQueue.empty()) {
    389             drainOneOutputBuffer(mFirstPictureId, mFirstPicture);
    390             delete[] mFirstPicture;
    391             mFirstPicture = NULL;
    392             mFirstPictureId = -1;
    393         }
    394 
    395         while (!outQueue.empty() &&
    396                 mHeadersDecoded &&
    397                 H264SwDecNextPicture(mHandle, &decodedPicture, 0)
    398                     == H264SWDEC_PIC_RDY) {
    399 
    400             int32_t picId = decodedPicture.picId;
    401             uint8_t *data = (uint8_t *) decodedPicture.pOutputPicture;
    402             drainOneOutputBuffer(picId, data);
    403         }
    404     }
    405 }
    406 
    407 bool SoftAVC::handlePortSettingChangeEvent(const H264SwDecInfo *info) {
    408     if (mWidth != info->picWidth || mHeight != info->picHeight) {
    409         mWidth  = info->picWidth;
    410         mHeight = info->picHeight;
    411         mPictureSize = mWidth * mHeight * 3 / 2;
    412         mCropWidth = mWidth;
    413         mCropHeight = mHeight;
    414         updatePortDefinitions();
    415         notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
    416         mOutputPortSettingsChange = AWAITING_DISABLED;
    417         return true;
    418     }
    419 
    420     return false;
    421 }
    422 
    423 bool SoftAVC::handleCropRectEvent(const CropParams *crop) {
    424     if (mCropLeft != crop->cropLeftOffset ||
    425         mCropTop != crop->cropTopOffset ||
    426         mCropWidth != crop->cropOutWidth ||
    427         mCropHeight != crop->cropOutHeight) {
    428         mCropLeft = crop->cropLeftOffset;
    429         mCropTop = crop->cropTopOffset;
    430         mCropWidth = crop->cropOutWidth;
    431         mCropHeight = crop->cropOutHeight;
    432 
    433         notify(OMX_EventPortSettingsChanged, 1,
    434                 OMX_IndexConfigCommonOutputCrop, NULL);
    435 
    436         return true;
    437     }
    438     return false;
    439 }
    440 
    441 void SoftAVC::saveFirstOutputBuffer(int32_t picId, uint8_t *data) {
    442     CHECK(mFirstPicture == NULL);
    443     mFirstPictureId = picId;
    444 
    445     mFirstPicture = new uint8_t[mPictureSize];
    446     memcpy(mFirstPicture, data, mPictureSize);
    447 }
    448 
    449 void SoftAVC::drainOneOutputBuffer(int32_t picId, uint8_t* data) {
    450     List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
    451     BufferInfo *outInfo = *outQueue.begin();
    452     outQueue.erase(outQueue.begin());
    453     OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
    454     OMX_BUFFERHEADERTYPE *header = mPicToHeaderMap.valueFor(picId);
    455     outHeader->nTimeStamp = header->nTimeStamp;
    456     outHeader->nFlags = header->nFlags;
    457     outHeader->nFilledLen = mPictureSize;
    458     memcpy(outHeader->pBuffer + outHeader->nOffset,
    459             data, mPictureSize);
    460     mPicToHeaderMap.removeItem(picId);
    461     delete header;
    462     outInfo->mOwnedByUs = false;
    463     notifyFillBufferDone(outHeader);
    464 }
    465 
    466 bool SoftAVC::drainAllOutputBuffers() {
    467     List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
    468     H264SwDecPicture decodedPicture;
    469 
    470     while (!outQueue.empty()) {
    471         BufferInfo *outInfo = *outQueue.begin();
    472         outQueue.erase(outQueue.begin());
    473         OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
    474         if (mHeadersDecoded &&
    475             H264SWDEC_PIC_RDY ==
    476                 H264SwDecNextPicture(mHandle, &decodedPicture, 1 /* flush */)) {
    477 
    478             int32_t picId = decodedPicture.picId;
    479             CHECK(mPicToHeaderMap.indexOfKey(picId) >= 0);
    480 
    481             memcpy(outHeader->pBuffer + outHeader->nOffset,
    482                 decodedPicture.pOutputPicture,
    483                 mPictureSize);
    484 
    485             OMX_BUFFERHEADERTYPE *header = mPicToHeaderMap.valueFor(picId);
    486             outHeader->nTimeStamp = header->nTimeStamp;
    487             outHeader->nFlags = header->nFlags;
    488             outHeader->nFilledLen = mPictureSize;
    489             mPicToHeaderMap.removeItem(picId);
    490             delete header;
    491         } else {
    492             outHeader->nTimeStamp = 0;
    493             outHeader->nFilledLen = 0;
    494             outHeader->nFlags = OMX_BUFFERFLAG_EOS;
    495             mEOSStatus = OUTPUT_FRAMES_FLUSHED;
    496         }
    497 
    498         outInfo->mOwnedByUs = false;
    499         notifyFillBufferDone(outHeader);
    500     }
    501 
    502     return true;
    503 }
    504 
    505 void SoftAVC::onPortFlushCompleted(OMX_U32 portIndex) {
    506     if (portIndex == kInputPortIndex) {
    507         mEOSStatus = INPUT_DATA_AVAILABLE;
    508     }
    509 }
    510 
    511 void SoftAVC::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
    512     switch (mOutputPortSettingsChange) {
    513         case NONE:
    514             break;
    515 
    516         case AWAITING_DISABLED:
    517         {
    518             CHECK(!enabled);
    519             mOutputPortSettingsChange = AWAITING_ENABLED;
    520             break;
    521         }
    522 
    523         default:
    524         {
    525             CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
    526             CHECK(enabled);
    527             mOutputPortSettingsChange = NONE;
    528             break;
    529         }
    530     }
    531 }
    532 
    533 void SoftAVC::updatePortDefinitions() {
    534     OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef;
    535     def->format.video.nFrameWidth = mWidth;
    536     def->format.video.nFrameHeight = mHeight;
    537     def->format.video.nStride = def->format.video.nFrameWidth;
    538     def->format.video.nSliceHeight = def->format.video.nFrameHeight;
    539 
    540     def = &editPortInfo(1)->mDef;
    541     def->format.video.nFrameWidth = mWidth;
    542     def->format.video.nFrameHeight = mHeight;
    543     def->format.video.nStride = def->format.video.nFrameWidth;
    544     def->format.video.nSliceHeight = def->format.video.nFrameHeight;
    545 
    546     def->nBufferSize =
    547         (def->format.video.nFrameWidth
    548             * def->format.video.nFrameHeight * 3) / 2;
    549 }
    550 
    551 }  // namespace android
    552 
    553 android::SoftOMXComponent *createSoftOMXComponent(
    554         const char *name, const OMX_CALLBACKTYPE *callbacks,
    555         OMX_PTR appData, OMX_COMPONENTTYPE **component) {
    556     return new android::SoftAVC(name, callbacks, appData, component);
    557 }
    558