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
32namespace android {
33
34template<class T>
35static 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
43SoftVideoDecoderOMXComponent::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
73void 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
133void 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
166uint32_t SoftVideoDecoderOMXComponent::outputBufferWidth() {
167    return mIsAdaptive ? mAdaptiveMaxWidth : mWidth;
168}
169
170uint32_t SoftVideoDecoderOMXComponent::outputBufferHeight() {
171    return mIsAdaptive ? mAdaptiveMaxHeight : mHeight;
172}
173
174void 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
230void 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
259OMX_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
314OMX_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
417OMX_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
441OMX_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
451void SoftVideoDecoderOMXComponent::onReset() {
452    mOutputPortSettingsChange = NONE;
453}
454
455void 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