SoftMPEG4.cpp revision f2af5a2c607e71ff4cd39da28b077c0a68b206fe
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 "SoftMPEG4"
19#include <utils/Log.h>
20
21#include "SoftMPEG4.h"
22
23#include <media/stagefright/foundation/ADebug.h>
24#include <media/stagefright/MediaDefs.h>
25#include <media/stagefright/MediaErrors.h>
26
27#include "mp4dec_api.h"
28
29namespace android {
30
31template<class T>
32static void InitOMXParams(T *params) {
33    params->nSize = sizeof(T);
34    params->nVersion.s.nVersionMajor = 1;
35    params->nVersion.s.nVersionMinor = 0;
36    params->nVersion.s.nRevision = 0;
37    params->nVersion.s.nStep = 0;
38}
39
40SoftMPEG4::SoftMPEG4(
41        const char *name,
42        const OMX_CALLBACKTYPE *callbacks,
43        OMX_PTR appData,
44        OMX_COMPONENTTYPE **component)
45    : SimpleSoftOMXComponent(name, callbacks, appData, component),
46      mMode(MODE_MPEG4),
47      mHandle(new tagvideoDecControls),
48      mInputBufferCount(0),
49      mWidth(352),
50      mHeight(288),
51      mCropLeft(0),
52      mCropTop(0),
53      mCropRight(mWidth - 1),
54      mCropBottom(mHeight - 1),
55      mSignalledError(false),
56      mInitialized(false),
57      mFramesConfigured(false),
58      mNumSamplesOutput(0),
59      mOutputPortSettingsChange(NONE) {
60    if (!strcmp(name, "OMX.google.h263.decoder")) {
61        mMode = MODE_H263;
62    } else {
63        CHECK(!strcmp(name, "OMX.google.mpeg4.decoder"));
64    }
65
66    initPorts();
67    CHECK_EQ(initDecoder(), (status_t)OK);
68}
69
70SoftMPEG4::~SoftMPEG4() {
71    if (mInitialized) {
72        PVCleanUpVideoDecoder(mHandle);
73    }
74
75    delete mHandle;
76    mHandle = NULL;
77}
78
79void SoftMPEG4::initPorts() {
80    OMX_PARAM_PORTDEFINITIONTYPE def;
81    InitOMXParams(&def);
82
83    def.nPortIndex = 0;
84    def.eDir = OMX_DirInput;
85    def.nBufferCountMin = kNumInputBuffers;
86    def.nBufferCountActual = def.nBufferCountMin;
87    def.nBufferSize = 8192;
88    def.bEnabled = OMX_TRUE;
89    def.bPopulated = OMX_FALSE;
90    def.eDomain = OMX_PortDomainVideo;
91    def.bBuffersContiguous = OMX_FALSE;
92    def.nBufferAlignment = 1;
93
94    def.format.video.cMIMEType =
95        (mMode == MODE_MPEG4)
96            ? const_cast<char *>(MEDIA_MIMETYPE_VIDEO_MPEG4)
97            : const_cast<char *>(MEDIA_MIMETYPE_VIDEO_H263);
98
99    def.format.video.pNativeRender = NULL;
100    def.format.video.nFrameWidth = mWidth;
101    def.format.video.nFrameHeight = mHeight;
102    def.format.video.nStride = def.format.video.nFrameWidth;
103    def.format.video.nSliceHeight = def.format.video.nFrameHeight;
104    def.format.video.nBitrate = 0;
105    def.format.video.xFramerate = 0;
106    def.format.video.bFlagErrorConcealment = OMX_FALSE;
107
108    def.format.video.eCompressionFormat =
109        mMode == MODE_MPEG4 ? OMX_VIDEO_CodingMPEG4 : OMX_VIDEO_CodingH263;
110
111    def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
112    def.format.video.pNativeWindow = NULL;
113
114    addPort(def);
115
116    def.nPortIndex = 1;
117    def.eDir = OMX_DirOutput;
118    def.nBufferCountMin = kNumOutputBuffers;
119    def.nBufferCountActual = def.nBufferCountMin;
120    def.bEnabled = OMX_TRUE;
121    def.bPopulated = OMX_FALSE;
122    def.eDomain = OMX_PortDomainVideo;
123    def.bBuffersContiguous = OMX_FALSE;
124    def.nBufferAlignment = 2;
125
126    def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW);
127    def.format.video.pNativeRender = NULL;
128    def.format.video.nFrameWidth = mWidth;
129    def.format.video.nFrameHeight = mHeight;
130    def.format.video.nStride = def.format.video.nFrameWidth;
131    def.format.video.nSliceHeight = def.format.video.nFrameHeight;
132    def.format.video.nBitrate = 0;
133    def.format.video.xFramerate = 0;
134    def.format.video.bFlagErrorConcealment = OMX_FALSE;
135    def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
136    def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
137    def.format.video.pNativeWindow = NULL;
138
139    def.nBufferSize =
140        (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2;
141
142    addPort(def);
143}
144
145status_t SoftMPEG4::initDecoder() {
146    memset(mHandle, 0, sizeof(tagvideoDecControls));
147    return OK;
148}
149
150OMX_ERRORTYPE SoftMPEG4::internalGetParameter(
151        OMX_INDEXTYPE index, OMX_PTR params) {
152    switch (index) {
153        case OMX_IndexParamVideoPortFormat:
154        {
155            OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
156                (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
157
158            if (formatParams->nPortIndex > 1) {
159                return OMX_ErrorUndefined;
160            }
161
162            if (formatParams->nIndex != 0) {
163                return OMX_ErrorNoMore;
164            }
165
166            if (formatParams->nPortIndex == 0) {
167                formatParams->eCompressionFormat =
168                    (mMode == MODE_MPEG4)
169                        ? OMX_VIDEO_CodingMPEG4 : OMX_VIDEO_CodingH263;
170
171                formatParams->eColorFormat = OMX_COLOR_FormatUnused;
172                formatParams->xFramerate = 0;
173            } else {
174                CHECK_EQ(formatParams->nPortIndex, 1u);
175
176                formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
177                formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
178                formatParams->xFramerate = 0;
179            }
180
181            return OMX_ErrorNone;
182        }
183
184        default:
185            return SimpleSoftOMXComponent::internalGetParameter(index, params);
186    }
187}
188
189OMX_ERRORTYPE SoftMPEG4::internalSetParameter(
190        OMX_INDEXTYPE index, const OMX_PTR params) {
191    switch (index) {
192        case OMX_IndexParamStandardComponentRole:
193        {
194            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
195                (const OMX_PARAM_COMPONENTROLETYPE *)params;
196
197            if (mMode == MODE_MPEG4) {
198                if (strncmp((const char *)roleParams->cRole,
199                            "video_decoder.mpeg4",
200                            OMX_MAX_STRINGNAME_SIZE - 1)) {
201                    return OMX_ErrorUndefined;
202                }
203            } else {
204                if (strncmp((const char *)roleParams->cRole,
205                            "video_decoder.h263",
206                            OMX_MAX_STRINGNAME_SIZE - 1)) {
207                    return OMX_ErrorUndefined;
208                }
209            }
210
211            return OMX_ErrorNone;
212        }
213
214        case OMX_IndexParamVideoPortFormat:
215        {
216            OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
217                (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
218
219            if (formatParams->nPortIndex > 1) {
220                return OMX_ErrorUndefined;
221            }
222
223            if (formatParams->nIndex != 0) {
224                return OMX_ErrorNoMore;
225            }
226
227            return OMX_ErrorNone;
228        }
229
230        default:
231            return SimpleSoftOMXComponent::internalSetParameter(index, params);
232    }
233}
234
235OMX_ERRORTYPE SoftMPEG4::getConfig(
236        OMX_INDEXTYPE index, OMX_PTR params) {
237    switch (index) {
238        case OMX_IndexConfigCommonOutputCrop:
239        {
240            OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params;
241
242            if (rectParams->nPortIndex != 1) {
243                return OMX_ErrorUndefined;
244            }
245
246            rectParams->nLeft = mCropLeft;
247            rectParams->nTop = mCropTop;
248            rectParams->nWidth = mCropRight - mCropLeft + 1;
249            rectParams->nHeight = mCropBottom - mCropTop + 1;
250
251            return OMX_ErrorNone;
252        }
253
254        default:
255            return OMX_ErrorUnsupportedIndex;
256    }
257}
258
259void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) {
260    if (mSignalledError || mOutputPortSettingsChange != NONE) {
261        return;
262    }
263
264    List<BufferInfo *> &inQueue = getPortQueue(0);
265    List<BufferInfo *> &outQueue = getPortQueue(1);
266
267    while (!inQueue.empty() && outQueue.size() == kNumOutputBuffers) {
268        BufferInfo *inInfo = *inQueue.begin();
269        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
270
271        PortInfo *port = editPortInfo(1);
272
273        OMX_BUFFERHEADERTYPE *outHeader =
274            port->mBuffers.editItemAt(mNumSamplesOutput & 1).mHeader;
275
276        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
277            inQueue.erase(inQueue.begin());
278            inInfo->mOwnedByUs = false;
279            notifyEmptyBufferDone(inHeader);
280
281            ++mInputBufferCount;
282
283            outHeader->nFilledLen = 0;
284            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
285
286            List<BufferInfo *>::iterator it = outQueue.begin();
287            while ((*it)->mHeader != outHeader) {
288                ++it;
289            }
290
291            BufferInfo *outInfo = *it;
292            outInfo->mOwnedByUs = false;
293            outQueue.erase(it);
294            outInfo = NULL;
295
296            notifyFillBufferDone(outHeader);
297            outHeader = NULL;
298            return;
299        }
300
301        uint8_t *bitstream = inHeader->pBuffer + inHeader->nOffset;
302
303        if (!mInitialized) {
304            uint8_t *vol_data[1];
305            int32_t vol_size = 0;
306
307            vol_data[0] = NULL;
308
309            if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
310                vol_data[0] = bitstream;
311                vol_size = inHeader->nFilledLen;
312            }
313
314            MP4DecodingMode mode =
315                (mMode == MODE_MPEG4) ? MPEG4_MODE : H263_MODE;
316
317            Bool success = PVInitVideoDecoder(
318                    mHandle, vol_data, &vol_size, 1, mWidth, mHeight, mode);
319
320            if (!success) {
321                LOGW("PVInitVideoDecoder failed. Unsupported content?");
322
323                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
324                mSignalledError = true;
325                return;
326            }
327
328            MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle);
329            if (mode != actualMode) {
330                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
331                mSignalledError = true;
332                return;
333            }
334
335            PVSetPostProcType((VideoDecControls *) mHandle, 0);
336
337            if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
338                inInfo->mOwnedByUs = false;
339                inQueue.erase(inQueue.begin());
340                inInfo = NULL;
341                notifyEmptyBufferDone(inHeader);
342                inHeader = NULL;
343            }
344
345            mInitialized = true;
346
347            if (mode == MPEG4_MODE && portSettingsChanged()) {
348                return;
349            }
350
351            continue;
352        }
353
354        if (!mFramesConfigured) {
355            PortInfo *port = editPortInfo(1);
356            OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(1).mHeader;
357
358            PVSetReferenceYUV(mHandle, outHeader->pBuffer);
359
360            mFramesConfigured = true;
361        }
362
363        uint32_t useExtTimestamp = (inHeader->nOffset == 0);
364
365        // decoder deals in ms, OMX in us.
366        uint32_t timestamp =
367            useExtTimestamp ? (inHeader->nTimeStamp + 500) / 1000 : 0xFFFFFFFF;
368
369        int32_t bufferSize = inHeader->nFilledLen;
370
371        if (PVDecodeVideoFrame(
372                    mHandle, &bitstream, &timestamp, &bufferSize,
373                    &useExtTimestamp,
374                    outHeader->pBuffer) != PV_TRUE) {
375            LOGE("failed to decode video frame.");
376
377            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
378            mSignalledError = true;
379            return;
380        }
381
382        if (portSettingsChanged()) {
383            return;
384        }
385
386        // decoder deals in ms, OMX in us.
387        outHeader->nTimeStamp = timestamp * 1000;
388
389        CHECK_LE(bufferSize, inHeader->nFilledLen);
390        inHeader->nOffset += inHeader->nFilledLen - bufferSize;
391        inHeader->nFilledLen = bufferSize;
392
393        if (inHeader->nFilledLen == 0) {
394            inInfo->mOwnedByUs = false;
395            inQueue.erase(inQueue.begin());
396            inInfo = NULL;
397            notifyEmptyBufferDone(inHeader);
398            inHeader = NULL;
399        }
400
401        ++mInputBufferCount;
402
403        outHeader->nOffset = 0;
404        outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
405        outHeader->nFlags = 0;
406
407        List<BufferInfo *>::iterator it = outQueue.begin();
408        while ((*it)->mHeader != outHeader) {
409            ++it;
410        }
411
412        BufferInfo *outInfo = *it;
413        outInfo->mOwnedByUs = false;
414        outQueue.erase(it);
415        outInfo = NULL;
416
417        notifyFillBufferDone(outHeader);
418        outHeader = NULL;
419
420        ++mNumSamplesOutput;
421    }
422}
423
424bool SoftMPEG4::portSettingsChanged() {
425    int32_t disp_width, disp_height;
426    PVGetVideoDimensions(mHandle, &disp_width, &disp_height);
427
428    int32_t buf_width, buf_height;
429    PVGetBufferDimensions(mHandle, &buf_width, &buf_height);
430
431    CHECK_LE(disp_width, buf_width);
432    CHECK_LE(disp_height, buf_height);
433
434    LOGV("disp_width = %d, disp_height = %d, buf_width = %d, buf_height = %d",
435            disp_width, disp_height, buf_width, buf_height);
436
437    if (mCropRight != disp_width - 1
438            || mCropBottom != disp_height - 1) {
439        mCropLeft = 0;
440        mCropTop = 0;
441        mCropRight = disp_width - 1;
442        mCropBottom = disp_height - 1;
443
444        notify(OMX_EventPortSettingsChanged,
445               1,
446               OMX_IndexConfigCommonOutputCrop,
447               NULL);
448    }
449
450    if (buf_width != mWidth || buf_height != mHeight) {
451        mWidth = buf_width;
452        mHeight = buf_height;
453
454        updatePortDefinitions();
455
456        if (mMode == MODE_H263) {
457            PVCleanUpVideoDecoder(mHandle);
458
459            uint8_t *vol_data[1];
460            int32_t vol_size = 0;
461
462            vol_data[0] = NULL;
463            if (!PVInitVideoDecoder(
464                    mHandle, vol_data, &vol_size, 1, mWidth, mHeight,
465                    H263_MODE)) {
466                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
467                mSignalledError = true;
468                return true;
469            }
470        }
471
472        mFramesConfigured = false;
473
474        notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
475        mOutputPortSettingsChange = AWAITING_DISABLED;
476        return true;
477    }
478
479    return false;
480}
481
482void SoftMPEG4::onPortFlushCompleted(OMX_U32 portIndex) {
483    if (portIndex == 0 && mInitialized) {
484        CHECK_EQ((int)PVResetVideoDecoder(mHandle), (int)PV_TRUE);
485    }
486}
487
488void SoftMPEG4::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
489    if (portIndex != 1) {
490        return;
491    }
492
493    switch (mOutputPortSettingsChange) {
494        case NONE:
495            break;
496
497        case AWAITING_DISABLED:
498        {
499            CHECK(!enabled);
500            mOutputPortSettingsChange = AWAITING_ENABLED;
501            break;
502        }
503
504        default:
505        {
506            CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
507            CHECK(enabled);
508            mOutputPortSettingsChange = NONE;
509            break;
510        }
511    }
512}
513
514void SoftMPEG4::updatePortDefinitions() {
515    OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef;
516    def->format.video.nFrameWidth = mWidth;
517    def->format.video.nFrameHeight = mHeight;
518    def->format.video.nStride = def->format.video.nFrameWidth;
519    def->format.video.nSliceHeight = def->format.video.nFrameHeight;
520
521    def = &editPortInfo(1)->mDef;
522    def->format.video.nFrameWidth = mWidth;
523    def->format.video.nFrameHeight = mHeight;
524    def->format.video.nStride = def->format.video.nFrameWidth;
525    def->format.video.nSliceHeight = def->format.video.nFrameHeight;
526
527    def->nBufferSize =
528        (((def->format.video.nFrameWidth + 15) & -16)
529            * ((def->format.video.nFrameHeight + 15) & -16) * 3) / 2;
530}
531
532}  // namespace android
533
534android::SoftOMXComponent *createSoftOMXComponent(
535        const char *name, const OMX_CALLBACKTYPE *callbacks,
536        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
537    return new android::SoftMPEG4(name, callbacks, appData, component);
538}
539
540