SoftMPEG4.cpp revision baa9146401e28c5acf54dea21ddd197f0d3a8fcd
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#include <media/IOMX.h>
27
28#include "mp4dec_api.h"
29
30namespace android {
31
32static const CodecProfileLevel kM4VProfileLevels[] = {
33    { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level0 },
34    { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level0b },
35    { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level1 },
36    { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level2 },
37    { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level3 },
38};
39
40static const CodecProfileLevel kH263ProfileLevels[] = {
41    { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level10 },
42    { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level20 },
43    { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level30 },
44    { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level45 },
45    { OMX_VIDEO_H263ProfileISWV2,    OMX_VIDEO_H263Level10 },
46    { OMX_VIDEO_H263ProfileISWV2,    OMX_VIDEO_H263Level20 },
47    { OMX_VIDEO_H263ProfileISWV2,    OMX_VIDEO_H263Level30 },
48    { OMX_VIDEO_H263ProfileISWV2,    OMX_VIDEO_H263Level45 },
49};
50
51SoftMPEG4::SoftMPEG4(
52        const char *name,
53        const char *componentRole,
54        OMX_VIDEO_CODINGTYPE codingType,
55        const CodecProfileLevel *profileLevels,
56        size_t numProfileLevels,
57        const OMX_CALLBACKTYPE *callbacks,
58        OMX_PTR appData,
59        OMX_COMPONENTTYPE **component)
60    : SoftVideoDecoderOMXComponent(
61            name, componentRole, codingType, profileLevels, numProfileLevels,
62            352 /* width */, 288 /* height */, callbacks, appData, component),
63      mMode(codingType == OMX_VIDEO_CodingH263 ? MODE_H263 : MODE_MPEG4),
64      mHandle(new tagvideoDecControls),
65      mInputBufferCount(0),
66      mSignalledError(false),
67      mInitialized(false),
68      mFramesConfigured(false),
69      mNumSamplesOutput(0),
70      mPvTime(0) {
71    initPorts(
72            kNumInputBuffers,
73            8192 /* inputBufferSize */,
74            kNumOutputBuffers,
75            (mMode == MODE_MPEG4)
76            ? MEDIA_MIMETYPE_VIDEO_MPEG4 : MEDIA_MIMETYPE_VIDEO_H263);
77    CHECK_EQ(initDecoder(), (status_t)OK);
78}
79
80SoftMPEG4::~SoftMPEG4() {
81    if (mInitialized) {
82        PVCleanUpVideoDecoder(mHandle);
83    }
84
85    delete mHandle;
86    mHandle = NULL;
87}
88
89status_t SoftMPEG4::initDecoder() {
90    memset(mHandle, 0, sizeof(tagvideoDecControls));
91    return OK;
92}
93
94void SoftMPEG4::onQueueFilled(OMX_U32 /* portIndex */) {
95    if (mSignalledError || mOutputPortSettingsChange != NONE) {
96        return;
97    }
98
99    List<BufferInfo *> &inQueue = getPortQueue(0);
100    List<BufferInfo *> &outQueue = getPortQueue(1);
101
102    while (!inQueue.empty() && outQueue.size() == kNumOutputBuffers) {
103        BufferInfo *inInfo = *inQueue.begin();
104        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
105
106        PortInfo *port = editPortInfo(1);
107
108        OMX_BUFFERHEADERTYPE *outHeader =
109            port->mBuffers.editItemAt(mNumSamplesOutput & 1).mHeader;
110
111        if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && inHeader->nFilledLen == 0) {
112            inQueue.erase(inQueue.begin());
113            inInfo->mOwnedByUs = false;
114            notifyEmptyBufferDone(inHeader);
115
116            ++mInputBufferCount;
117
118            outHeader->nFilledLen = 0;
119            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
120
121            List<BufferInfo *>::iterator it = outQueue.begin();
122            while ((*it)->mHeader != outHeader) {
123                ++it;
124            }
125
126            BufferInfo *outInfo = *it;
127            outInfo->mOwnedByUs = false;
128            outQueue.erase(it);
129            outInfo = NULL;
130
131            notifyFillBufferDone(outHeader);
132            outHeader = NULL;
133            return;
134        }
135
136        uint8_t *bitstream = inHeader->pBuffer + inHeader->nOffset;
137        uint32_t *start_code = (uint32_t *)bitstream;
138        bool volHeader = *start_code == 0xB0010000;
139        if (volHeader) {
140            PVCleanUpVideoDecoder(mHandle);
141            mInitialized = false;
142        }
143
144        if (!mInitialized) {
145            uint8_t *vol_data[1];
146            int32_t vol_size = 0;
147
148            vol_data[0] = NULL;
149
150            if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) || volHeader) {
151                vol_data[0] = bitstream;
152                vol_size = inHeader->nFilledLen;
153            }
154
155            MP4DecodingMode mode =
156                (mMode == MODE_MPEG4) ? MPEG4_MODE : H263_MODE;
157
158            Bool success = PVInitVideoDecoder(
159                    mHandle, vol_data, &vol_size, 1,
160                    outputBufferWidth(), outputBufferHeight(), mode);
161
162            if (!success) {
163                ALOGW("PVInitVideoDecoder failed. Unsupported content?");
164
165                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
166                mSignalledError = true;
167                return;
168            }
169
170            MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle);
171            if (mode != actualMode) {
172                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
173                mSignalledError = true;
174                return;
175            }
176
177            PVSetPostProcType((VideoDecControls *) mHandle, 0);
178
179            bool hasFrameData = false;
180            if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
181                inInfo->mOwnedByUs = false;
182                inQueue.erase(inQueue.begin());
183                inInfo = NULL;
184                notifyEmptyBufferDone(inHeader);
185                inHeader = NULL;
186            } else if (volHeader) {
187                hasFrameData = true;
188            }
189
190            mInitialized = true;
191
192            if (mode == MPEG4_MODE && handlePortSettingsChange()) {
193                return;
194            }
195
196            if (!hasFrameData) {
197                continue;
198            }
199        }
200
201        if (!mFramesConfigured) {
202            PortInfo *port = editPortInfo(1);
203            OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(1).mHeader;
204
205            OMX_U32 yFrameSize = sizeof(uint8) * mHandle->size;
206            if ((outHeader->nAllocLen < yFrameSize) ||
207                    (outHeader->nAllocLen - yFrameSize < yFrameSize / 2)) {
208                ALOGE("Too small output buffer for reference frame: %lu bytes",
209                        (unsigned long)outHeader->nAllocLen);
210                android_errorWriteLog(0x534e4554, "30033990");
211                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
212                mSignalledError = true;
213                return;
214            }
215            PVSetReferenceYUV(mHandle, outHeader->pBuffer);
216            mFramesConfigured = true;
217        }
218
219        uint32_t useExtTimestamp = (inHeader->nOffset == 0);
220
221        // decoder deals in ms (int32_t), OMX in us (int64_t)
222        // so use fake timestamp instead
223        uint32_t timestamp = 0xFFFFFFFF;
224        if (useExtTimestamp) {
225            mPvToOmxTimeMap.add(mPvTime, inHeader->nTimeStamp);
226            timestamp = mPvTime;
227            mPvTime++;
228        }
229
230        int32_t bufferSize = inHeader->nFilledLen;
231        int32_t tmp = bufferSize;
232
233        OMX_U32 frameSize;
234        OMX_U64 yFrameSize = (OMX_U64)mWidth * (OMX_U64)mHeight;
235        if (yFrameSize > ((OMX_U64)UINT32_MAX / 3) * 2) {
236            ALOGE("Frame size too large");
237            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
238            mSignalledError = true;
239            return;
240        }
241        frameSize = (OMX_U32)(yFrameSize + (yFrameSize / 2));
242
243        if (outHeader->nAllocLen < frameSize) {
244            android_errorWriteLog(0x534e4554, "27833616");
245            ALOGE("Insufficient output buffer size");
246            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
247            mSignalledError = true;
248            return;
249        }
250        // The PV decoder is lying to us, sometimes it'll claim to only have
251        // consumed a subset of the buffer when it clearly consumed all of it.
252        // ignore whatever it says...
253        if (PVDecodeVideoFrame(
254                    mHandle, &bitstream, &timestamp, &tmp,
255                    &useExtTimestamp,
256                    outHeader->pBuffer) != PV_TRUE) {
257            ALOGE("failed to decode video frame.");
258
259            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
260            mSignalledError = true;
261            return;
262        }
263
264        // H263 doesn't have VOL header, the frame size information is in short header, i.e. the
265        // decoder may detect size change after PVDecodeVideoFrame.
266        if (handlePortSettingsChange()) {
267            return;
268        }
269
270        // decoder deals in ms, OMX in us.
271        outHeader->nTimeStamp = mPvToOmxTimeMap.valueFor(timestamp);
272        mPvToOmxTimeMap.removeItem(timestamp);
273
274        inHeader->nOffset += bufferSize;
275        inHeader->nFilledLen = 0;
276        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
277            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
278        } else {
279            outHeader->nFlags = 0;
280        }
281
282        if (inHeader->nFilledLen == 0) {
283            inInfo->mOwnedByUs = false;
284            inQueue.erase(inQueue.begin());
285            inInfo = NULL;
286            notifyEmptyBufferDone(inHeader);
287            inHeader = NULL;
288        }
289
290        ++mInputBufferCount;
291
292        outHeader->nOffset = 0;
293        outHeader->nFilledLen = frameSize;
294
295        List<BufferInfo *>::iterator it = outQueue.begin();
296        while ((*it)->mHeader != outHeader) {
297            ++it;
298        }
299
300        BufferInfo *outInfo = *it;
301        outInfo->mOwnedByUs = false;
302        outQueue.erase(it);
303        outInfo = NULL;
304
305        notifyFillBufferDone(outHeader);
306        outHeader = NULL;
307
308        ++mNumSamplesOutput;
309    }
310}
311
312bool SoftMPEG4::handlePortSettingsChange() {
313    uint32_t disp_width, disp_height;
314    PVGetVideoDimensions(mHandle, (int32 *)&disp_width, (int32 *)&disp_height);
315
316    uint32_t buf_width, buf_height;
317    PVGetBufferDimensions(mHandle, (int32 *)&buf_width, (int32 *)&buf_height);
318
319    CHECK_LE(disp_width, buf_width);
320    CHECK_LE(disp_height, buf_height);
321
322    ALOGV("disp_width = %d, disp_height = %d, buf_width = %d, buf_height = %d",
323            disp_width, disp_height, buf_width, buf_height);
324
325    CropSettingsMode cropSettingsMode = kCropUnSet;
326    if (disp_width != buf_width || disp_height != buf_height) {
327        cropSettingsMode = kCropSet;
328
329        if (mCropWidth != disp_width || mCropHeight != disp_height) {
330            mCropLeft = 0;
331            mCropTop = 0;
332            mCropWidth = disp_width;
333            mCropHeight = disp_height;
334            cropSettingsMode = kCropChanged;
335        }
336    }
337
338    bool portWillReset = false;
339    const bool fakeStride = true;
340    SoftVideoDecoderOMXComponent::handlePortSettingsChange(
341            &portWillReset, buf_width, buf_height, cropSettingsMode, fakeStride);
342    if (portWillReset) {
343        if (mMode == MODE_H263) {
344            PVCleanUpVideoDecoder(mHandle);
345
346            uint8_t *vol_data[1];
347            int32_t vol_size = 0;
348
349            vol_data[0] = NULL;
350            if (!PVInitVideoDecoder(
351                    mHandle, vol_data, &vol_size, 1, outputBufferWidth(), outputBufferHeight(),
352                    H263_MODE)) {
353                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
354                mSignalledError = true;
355                return true;
356            }
357        }
358
359        mFramesConfigured = false;
360    }
361
362    return portWillReset;
363}
364
365void SoftMPEG4::onPortFlushCompleted(OMX_U32 portIndex) {
366    if (portIndex == 0 && mInitialized) {
367        CHECK_EQ((int)PVResetVideoDecoder(mHandle), (int)PV_TRUE);
368    }
369}
370
371void SoftMPEG4::onReset() {
372    SoftVideoDecoderOMXComponent::onReset();
373    mPvToOmxTimeMap.clear();
374    mSignalledError = false;
375    mFramesConfigured = false;
376    if (mInitialized) {
377        PVCleanUpVideoDecoder(mHandle);
378        mInitialized = false;
379    }
380}
381
382void SoftMPEG4::updatePortDefinitions() {
383    SoftVideoDecoderOMXComponent::updatePortDefinitions();
384
385    /* We have to align our width and height - this should affect stride! */
386    OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef;
387    def->nBufferSize =
388        (((def->format.video.nFrameWidth + 15) & -16)
389            * ((def->format.video.nFrameHeight + 15) & -16) * 3) / 2;
390}
391
392}  // namespace android
393
394android::SoftOMXComponent *createSoftOMXComponent(
395        const char *name, const OMX_CALLBACKTYPE *callbacks,
396        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
397    using namespace android;
398    if (!strcmp(name, "OMX.google.h263.decoder")) {
399        return new android::SoftMPEG4(
400                name, "video_decoder.h263", OMX_VIDEO_CodingH263,
401                kH263ProfileLevels, ARRAY_SIZE(kH263ProfileLevels),
402                callbacks, appData, component);
403    } else if (!strcmp(name, "OMX.google.mpeg4.decoder")) {
404        return new android::SoftMPEG4(
405                name, "video_decoder.mpeg4", OMX_VIDEO_CodingMPEG4,
406                kM4VProfileLevels, ARRAY_SIZE(kM4VProfileLevels),
407                callbacks, appData, component);
408    } else {
409        CHECK(!"Unknown component");
410    }
411}
412
413