Home | History | Annotate | Download | only in omx
      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 #include <inttypes.h>
     18 
     19 //#define LOG_NDEBUG 0
     20 #define LOG_TAG "SoftVideoDecoderOMXComponent"
     21 #include <utils/Log.h>
     22 
     23 #include "include/SoftVideoDecoderOMXComponent.h"
     24 
     25 #include <media/hardware/HardwareAPI.h>
     26 #include <media/stagefright/foundation/ADebug.h>
     27 #include <media/stagefright/foundation/ALooper.h>
     28 #include <media/stagefright/foundation/AMessage.h>
     29 #include <media/stagefright/foundation/AUtils.h>
     30 #include <media/stagefright/MediaDefs.h>
     31 
     32 namespace android {
     33 
     34 template<class T>
     35 static void InitOMXParams(T *params) {
     36     params->nSize = sizeof(T);
     37     params->nVersion.s.nVersionMajor = 1;
     38     params->nVersion.s.nVersionMinor = 0;
     39     params->nVersion.s.nRevision = 0;
     40     params->nVersion.s.nStep = 0;
     41 }
     42 
     43 SoftVideoDecoderOMXComponent::SoftVideoDecoderOMXComponent(
     44         const char *name,
     45         const char *componentRole,
     46         OMX_VIDEO_CODINGTYPE codingType,
     47         const CodecProfileLevel *profileLevels,
     48         size_t numProfileLevels,
     49         int32_t width,
     50         int32_t height,
     51         const OMX_CALLBACKTYPE *callbacks,
     52         OMX_PTR appData,
     53         OMX_COMPONENTTYPE **component)
     54         : SimpleSoftOMXComponent(name, callbacks, appData, component),
     55         mIsAdaptive(false),
     56         mAdaptiveMaxWidth(0),
     57         mAdaptiveMaxHeight(0),
     58         mWidth(width),
     59         mHeight(height),
     60         mCropLeft(0),
     61         mCropTop(0),
     62         mCropWidth(width),
     63         mCropHeight(height),
     64         mOutputPortSettingsChange(NONE),
     65         mMinInputBufferSize(384), // arbitrary, using one uncompressed macroblock
     66         mMinCompressionRatio(1),  // max input size is normally the output size
     67         mComponentRole(componentRole),
     68         mCodingType(codingType),
     69         mProfileLevels(profileLevels),
     70         mNumProfileLevels(numProfileLevels) {
     71 }
     72 
     73 void SoftVideoDecoderOMXComponent::initPorts(
     74         OMX_U32 numInputBuffers,
     75         OMX_U32 inputBufferSize,
     76         OMX_U32 numOutputBuffers,
     77         const char *mimeType,
     78         OMX_U32 minCompressionRatio) {
     79     mMinInputBufferSize = inputBufferSize;
     80     mMinCompressionRatio = minCompressionRatio;
     81 
     82     OMX_PARAM_PORTDEFINITIONTYPE def;
     83     InitOMXParams(&def);
     84 
     85     def.nPortIndex = kInputPortIndex;
     86     def.eDir = OMX_DirInput;
     87     def.nBufferCountMin = numInputBuffers;
     88     def.nBufferCountActual = def.nBufferCountMin;
     89     def.nBufferSize = inputBufferSize;
     90     def.bEnabled = OMX_TRUE;
     91     def.bPopulated = OMX_FALSE;
     92     def.eDomain = OMX_PortDomainVideo;
     93     def.bBuffersContiguous = OMX_FALSE;
     94     def.nBufferAlignment = 1;
     95 
     96     def.format.video.cMIMEType = const_cast<char *>(mimeType);
     97     def.format.video.pNativeRender = NULL;
     98     /* size is initialized in updatePortDefinitions() */
     99     def.format.video.nBitrate = 0;
    100     def.format.video.xFramerate = 0;
    101     def.format.video.bFlagErrorConcealment = OMX_FALSE;
    102     def.format.video.eCompressionFormat = mCodingType;
    103     def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
    104     def.format.video.pNativeWindow = NULL;
    105 
    106     addPort(def);
    107 
    108     def.nPortIndex = kOutputPortIndex;
    109     def.eDir = OMX_DirOutput;
    110     def.nBufferCountMin = numOutputBuffers;
    111     def.nBufferCountActual = def.nBufferCountMin;
    112     def.bEnabled = OMX_TRUE;
    113     def.bPopulated = OMX_FALSE;
    114     def.eDomain = OMX_PortDomainVideo;
    115     def.bBuffersContiguous = OMX_FALSE;
    116     def.nBufferAlignment = 2;
    117 
    118     def.format.video.cMIMEType = const_cast<char *>("video/raw");
    119     def.format.video.pNativeRender = NULL;
    120     /* size is initialized in updatePortDefinitions() */
    121     def.format.video.nBitrate = 0;
    122     def.format.video.xFramerate = 0;
    123     def.format.video.bFlagErrorConcealment = OMX_FALSE;
    124     def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
    125     def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
    126     def.format.video.pNativeWindow = NULL;
    127 
    128     addPort(def);
    129 
    130     updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */);
    131 }
    132 
    133 void SoftVideoDecoderOMXComponent::updatePortDefinitions(bool updateCrop, bool updateInputSize) {
    134     OMX_PARAM_PORTDEFINITIONTYPE *outDef = &editPortInfo(kOutputPortIndex)->mDef;
    135     outDef->format.video.nFrameWidth = outputBufferWidth();
    136     outDef->format.video.nFrameHeight = outputBufferHeight();
    137     outDef->format.video.nStride = outDef->format.video.nFrameWidth;
    138     outDef->format.video.nSliceHeight = outDef->format.video.nFrameHeight;
    139 
    140     outDef->nBufferSize =
    141         (outDef->format.video.nStride * outDef->format.video.nSliceHeight * 3) / 2;
    142 
    143     OMX_PARAM_PORTDEFINITIONTYPE *inDef = &editPortInfo(kInputPortIndex)->mDef;
    144     inDef->format.video.nFrameWidth = mWidth;
    145     inDef->format.video.nFrameHeight = mHeight;
    146     // input port is compressed, hence it has no stride
    147     inDef->format.video.nStride = 0;
    148     inDef->format.video.nSliceHeight = 0;
    149 
    150     // when output format changes, input buffer size does not actually change
    151     if (updateInputSize) {
    152         inDef->nBufferSize = max(
    153                 outDef->nBufferSize / mMinCompressionRatio,
    154                 max(mMinInputBufferSize, inDef->nBufferSize));
    155     }
    156 
    157     if (updateCrop) {
    158         mCropLeft = 0;
    159         mCropTop = 0;
    160         mCropWidth = mWidth;
    161         mCropHeight = mHeight;
    162     }
    163 }
    164 
    165 
    166 uint32_t SoftVideoDecoderOMXComponent::outputBufferWidth() {
    167     return mIsAdaptive ? mAdaptiveMaxWidth : mWidth;
    168 }
    169 
    170 uint32_t SoftVideoDecoderOMXComponent::outputBufferHeight() {
    171     return mIsAdaptive ? mAdaptiveMaxHeight : mHeight;
    172 }
    173 
    174 void SoftVideoDecoderOMXComponent::handlePortSettingsChange(
    175         bool *portWillReset, uint32_t width, uint32_t height,
    176         CropSettingsMode cropSettingsMode, bool fakeStride) {
    177     *portWillReset = false;
    178     bool sizeChanged = (width != mWidth || height != mHeight);
    179     bool updateCrop = (cropSettingsMode == kCropUnSet);
    180     bool cropChanged = (cropSettingsMode == kCropChanged);
    181     bool strideChanged = false;
    182     if (fakeStride) {
    183         OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef;
    184         if (def->format.video.nStride != (OMX_S32)width
    185                 || def->format.video.nSliceHeight != (OMX_U32)height) {
    186             strideChanged = true;
    187         }
    188     }
    189 
    190     if (sizeChanged || cropChanged || strideChanged) {
    191         mWidth = width;
    192         mHeight = height;
    193 
    194         if ((sizeChanged && !mIsAdaptive)
    195             || width > mAdaptiveMaxWidth
    196             || height > mAdaptiveMaxHeight) {
    197             if (mIsAdaptive) {
    198                 if (width > mAdaptiveMaxWidth) {
    199                     mAdaptiveMaxWidth = width;
    200                 }
    201                 if (height > mAdaptiveMaxHeight) {
    202                     mAdaptiveMaxHeight = height;
    203                 }
    204             }
    205             updatePortDefinitions(updateCrop);
    206             notify(OMX_EventPortSettingsChanged, kOutputPortIndex, 0, NULL);
    207             mOutputPortSettingsChange = AWAITING_DISABLED;
    208             *portWillReset = true;
    209         } else {
    210             updatePortDefinitions(updateCrop);
    211 
    212             if (fakeStride) {
    213                 // MAJOR HACK that is not pretty, it's just to fool the renderer to read the correct
    214                 // data.
    215                 // Some software decoders (e.g. SoftMPEG4) fill decoded frame directly to output
    216                 // buffer without considering the output buffer stride and slice height. So this is
    217                 // used to signal how the buffer is arranged.  The alternative is to re-arrange the
    218                 // output buffer in SoftMPEG4, but that results in memcopies.
    219                 OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef;
    220                 def->format.video.nStride = mWidth;
    221                 def->format.video.nSliceHeight = mHeight;
    222             }
    223 
    224             notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
    225                    OMX_IndexConfigCommonOutputCrop, NULL);
    226         }
    227     }
    228 }
    229 
    230 void SoftVideoDecoderOMXComponent::copyYV12FrameToOutputBuffer(
    231         uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
    232         size_t srcYStride, size_t srcUStride, size_t srcVStride) {
    233     size_t dstYStride = outputBufferWidth();
    234     size_t dstUVStride = dstYStride / 2;
    235     size_t dstHeight = outputBufferHeight();
    236     uint8_t *dstStart = dst;
    237 
    238     for (size_t i = 0; i < mHeight; ++i) {
    239          memcpy(dst, srcY, mWidth);
    240          srcY += srcYStride;
    241          dst += dstYStride;
    242     }
    243 
    244     dst = dstStart + dstYStride * dstHeight;
    245     for (size_t i = 0; i < mHeight / 2; ++i) {
    246          memcpy(dst, srcU, mWidth / 2);
    247          srcU += srcUStride;
    248          dst += dstUVStride;
    249     }
    250 
    251     dst = dstStart + (5 * dstYStride * dstHeight) / 4;
    252     for (size_t i = 0; i < mHeight / 2; ++i) {
    253          memcpy(dst, srcV, mWidth / 2);
    254          srcV += srcVStride;
    255          dst += dstUVStride;
    256     }
    257 }
    258 
    259 OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalGetParameter(
    260         OMX_INDEXTYPE index, OMX_PTR params) {
    261     switch (index) {
    262         case OMX_IndexParamVideoPortFormat:
    263         {
    264             OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
    265                 (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
    266 
    267             if (formatParams->nPortIndex > kMaxPortIndex) {
    268                 return OMX_ErrorBadPortIndex;
    269             }
    270 
    271             if (formatParams->nIndex != 0) {
    272                 return OMX_ErrorNoMore;
    273             }
    274 
    275             if (formatParams->nPortIndex == kInputPortIndex) {
    276                 formatParams->eCompressionFormat = mCodingType;
    277                 formatParams->eColorFormat = OMX_COLOR_FormatUnused;
    278                 formatParams->xFramerate = 0;
    279             } else {
    280                 CHECK_EQ(formatParams->nPortIndex, 1u);
    281 
    282                 formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
    283                 formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
    284                 formatParams->xFramerate = 0;
    285             }
    286 
    287             return OMX_ErrorNone;
    288         }
    289 
    290         case OMX_IndexParamVideoProfileLevelQuerySupported:
    291         {
    292             OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel =
    293                   (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) params;
    294 
    295             if (profileLevel->nPortIndex != kInputPortIndex) {
    296                 ALOGE("Invalid port index: %" PRIu32, profileLevel->nPortIndex);
    297                 return OMX_ErrorUnsupportedIndex;
    298             }
    299 
    300             if (profileLevel->nProfileIndex >= mNumProfileLevels) {
    301                 return OMX_ErrorNoMore;
    302             }
    303 
    304             profileLevel->eProfile = mProfileLevels[profileLevel->nProfileIndex].mProfile;
    305             profileLevel->eLevel   = mProfileLevels[profileLevel->nProfileIndex].mLevel;
    306             return OMX_ErrorNone;
    307         }
    308 
    309         default:
    310             return SimpleSoftOMXComponent::internalGetParameter(index, params);
    311     }
    312 }
    313 
    314 OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalSetParameter(
    315         OMX_INDEXTYPE index, const OMX_PTR params) {
    316     // Include extension index OMX_INDEXEXTTYPE.
    317     const int32_t indexFull = index;
    318 
    319     switch (indexFull) {
    320         case OMX_IndexParamStandardComponentRole:
    321         {
    322             const OMX_PARAM_COMPONENTROLETYPE *roleParams =
    323                 (const OMX_PARAM_COMPONENTROLETYPE *)params;
    324 
    325             if (strncmp((const char *)roleParams->cRole,
    326                         mComponentRole,
    327                         OMX_MAX_STRINGNAME_SIZE - 1)) {
    328                 return OMX_ErrorUndefined;
    329             }
    330 
    331             return OMX_ErrorNone;
    332         }
    333 
    334         case OMX_IndexParamVideoPortFormat:
    335         {
    336             OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
    337                 (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
    338 
    339             if (formatParams->nPortIndex > kMaxPortIndex) {
    340                 return OMX_ErrorBadPortIndex;
    341             }
    342 
    343             if (formatParams->nIndex != 0) {
    344                 return OMX_ErrorNoMore;
    345             }
    346 
    347             if (formatParams->nPortIndex == kInputPortIndex) {
    348                 if (formatParams->eCompressionFormat != mCodingType
    349                         || formatParams->eColorFormat != OMX_COLOR_FormatUnused) {
    350                     return OMX_ErrorUnsupportedSetting;
    351                 }
    352             } else {
    353                 if (formatParams->eCompressionFormat != OMX_VIDEO_CodingUnused
    354                         || formatParams->eColorFormat != OMX_COLOR_FormatYUV420Planar) {
    355                     return OMX_ErrorUnsupportedSetting;
    356                 }
    357             }
    358 
    359             return OMX_ErrorNone;
    360         }
    361 
    362         case kPrepareForAdaptivePlaybackIndex:
    363         {
    364             const PrepareForAdaptivePlaybackParams* adaptivePlaybackParams =
    365                     (const PrepareForAdaptivePlaybackParams *)params;
    366             mIsAdaptive = adaptivePlaybackParams->bEnable;
    367             if (mIsAdaptive) {
    368                 mAdaptiveMaxWidth = adaptivePlaybackParams->nMaxFrameWidth;
    369                 mAdaptiveMaxHeight = adaptivePlaybackParams->nMaxFrameHeight;
    370                 mWidth = mAdaptiveMaxWidth;
    371                 mHeight = mAdaptiveMaxHeight;
    372             } else {
    373                 mAdaptiveMaxWidth = 0;
    374                 mAdaptiveMaxHeight = 0;
    375             }
    376             updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */);
    377             return OMX_ErrorNone;
    378         }
    379 
    380         case OMX_IndexParamPortDefinition:
    381         {
    382             OMX_PARAM_PORTDEFINITIONTYPE *newParams =
    383                 (OMX_PARAM_PORTDEFINITIONTYPE *)params;
    384             OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &newParams->format.video;
    385             OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(newParams->nPortIndex)->mDef;
    386 
    387             uint32_t oldWidth = def->format.video.nFrameWidth;
    388             uint32_t oldHeight = def->format.video.nFrameHeight;
    389             uint32_t newWidth = video_def->nFrameWidth;
    390             uint32_t newHeight = video_def->nFrameHeight;
    391             if (newWidth != oldWidth || newHeight != oldHeight) {
    392                 bool outputPort = (newParams->nPortIndex == kOutputPortIndex);
    393                 if (outputPort) {
    394                     // only update (essentially crop) if size changes
    395                     mWidth = newWidth;
    396                     mHeight = newHeight;
    397 
    398                     updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */);
    399                     // reset buffer size based on frame size
    400                     newParams->nBufferSize = def->nBufferSize;
    401                 } else {
    402                     // For input port, we only set nFrameWidth and nFrameHeight. Buffer size
    403                     // is updated when configuring the output port using the max-frame-size,
    404                     // though client can still request a larger size.
    405                     def->format.video.nFrameWidth = newWidth;
    406                     def->format.video.nFrameHeight = newHeight;
    407                 }
    408             }
    409             return SimpleSoftOMXComponent::internalSetParameter(index, params);
    410         }
    411 
    412         default:
    413             return SimpleSoftOMXComponent::internalSetParameter(index, params);
    414     }
    415 }
    416 
    417 OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getConfig(
    418         OMX_INDEXTYPE index, OMX_PTR params) {
    419     switch (index) {
    420         case OMX_IndexConfigCommonOutputCrop:
    421         {
    422             OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params;
    423 
    424             if (rectParams->nPortIndex != kOutputPortIndex) {
    425                 return OMX_ErrorUndefined;
    426             }
    427 
    428             rectParams->nLeft = mCropLeft;
    429             rectParams->nTop = mCropTop;
    430             rectParams->nWidth = mCropWidth;
    431             rectParams->nHeight = mCropHeight;
    432 
    433             return OMX_ErrorNone;
    434         }
    435 
    436         default:
    437             return OMX_ErrorUnsupportedIndex;
    438     }
    439 }
    440 
    441 OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getExtensionIndex(
    442         const char *name, OMX_INDEXTYPE *index) {
    443     if (!strcmp(name, "OMX.google.android.index.prepareForAdaptivePlayback")) {
    444         *(int32_t*)index = kPrepareForAdaptivePlaybackIndex;
    445         return OMX_ErrorNone;
    446     }
    447 
    448     return SimpleSoftOMXComponent::getExtensionIndex(name, index);
    449 }
    450 
    451 void SoftVideoDecoderOMXComponent::onReset() {
    452     mOutputPortSettingsChange = NONE;
    453 }
    454 
    455 void SoftVideoDecoderOMXComponent::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
    456     if (portIndex != kOutputPortIndex) {
    457         return;
    458     }
    459 
    460     switch (mOutputPortSettingsChange) {
    461         case NONE:
    462             break;
    463 
    464         case AWAITING_DISABLED:
    465         {
    466             CHECK(!enabled);
    467             mOutputPortSettingsChange = AWAITING_ENABLED;
    468             break;
    469         }
    470 
    471         default:
    472         {
    473             CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
    474             CHECK(enabled);
    475             mOutputPortSettingsChange = NONE;
    476             break;
    477         }
    478     }
    479 }
    480 
    481 }  // namespace android
    482