SoftAVC.cpp revision e75d53abb2f986cf07476d1430eb9981a79b2d2f
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
29namespace android {
30
31static 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
50SoftAVC::SoftAVC(
51        const char *name,
52        const OMX_CALLBACKTYPE *callbacks,
53        OMX_PTR appData,
54        OMX_COMPONENTTYPE **component)
55    : SoftVideoDecoderOMXComponent(
56            name, "video_decoder.avc", OMX_VIDEO_CodingAVC,
57            kProfileLevels, ARRAY_SIZE(kProfileLevels),
58            320 /* width */, 240 /* height */, callbacks, appData, component),
59      mHandle(NULL),
60      mInputBufferCount(0),
61      mPictureSize(mWidth * mHeight * 3 / 2),
62      mFirstPicture(NULL),
63      mFirstPictureId(-1),
64      mPicId(0),
65      mHeadersDecoded(false),
66      mEOSStatus(INPUT_DATA_AVAILABLE),
67      mSignalledError(false) {
68    initPorts(
69            kNumInputBuffers, 8192 /* inputBufferSize */,
70            kNumOutputBuffers, MEDIA_MIMETYPE_VIDEO_AVC);
71
72    CHECK_EQ(initDecoder(), (status_t)OK);
73}
74
75SoftAVC::~SoftAVC() {
76    H264SwDecRelease(mHandle);
77    mHandle = NULL;
78
79    while (mPicToHeaderMap.size() != 0) {
80        OMX_BUFFERHEADERTYPE *header = mPicToHeaderMap.editValueAt(0);
81        mPicToHeaderMap.removeItemsAt(0);
82        delete header;
83        header = NULL;
84    }
85    List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
86    List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex);
87    CHECK(outQueue.empty());
88    CHECK(inQueue.empty());
89
90    delete[] mFirstPicture;
91}
92
93status_t SoftAVC::initDecoder() {
94    // Force decoder to output buffers in display order.
95    if (H264SwDecInit(&mHandle, 0) == H264SWDEC_OK) {
96        return OK;
97    }
98    return UNKNOWN_ERROR;
99}
100
101void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
102    if (mSignalledError || mOutputPortSettingsChange != NONE) {
103        return;
104    }
105
106    if (mEOSStatus == OUTPUT_FRAMES_FLUSHED) {
107        return;
108    }
109
110    List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex);
111    List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
112
113    if (mHeadersDecoded) {
114        // Dequeue any already decoded output frames to free up space
115        // in the output queue.
116
117        drainAllOutputBuffers(false /* eos */);
118    }
119
120    H264SwDecRet ret = H264SWDEC_PIC_RDY;
121    bool portSettingsChanged = false;
122    while ((mEOSStatus != INPUT_DATA_AVAILABLE || !inQueue.empty())
123            && outQueue.size() == kNumOutputBuffers) {
124
125        if (mEOSStatus == INPUT_EOS_SEEN) {
126            drainAllOutputBuffers(true /* eos */);
127            return;
128        }
129
130        BufferInfo *inInfo = *inQueue.begin();
131        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
132        ++mPicId;
133
134        OMX_BUFFERHEADERTYPE *header = new OMX_BUFFERHEADERTYPE;
135        memset(header, 0, sizeof(OMX_BUFFERHEADERTYPE));
136        header->nTimeStamp = inHeader->nTimeStamp;
137        header->nFlags = inHeader->nFlags;
138        if (header->nFlags & OMX_BUFFERFLAG_EOS) {
139            mEOSStatus = INPUT_EOS_SEEN;
140        }
141        mPicToHeaderMap.add(mPicId, header);
142        inQueue.erase(inQueue.begin());
143
144        H264SwDecInput inPicture;
145        H264SwDecOutput outPicture;
146        memset(&inPicture, 0, sizeof(inPicture));
147        inPicture.dataLen = inHeader->nFilledLen;
148        inPicture.pStream = inHeader->pBuffer + inHeader->nOffset;
149        inPicture.picId = mPicId;
150        inPicture.intraConcealmentMethod = 1;
151        H264SwDecPicture decodedPicture;
152
153        while (inPicture.dataLen > 0) {
154            ret = H264SwDecDecode(mHandle, &inPicture, &outPicture);
155            if (ret == H264SWDEC_HDRS_RDY_BUFF_NOT_EMPTY ||
156                ret == H264SWDEC_PIC_RDY_BUFF_NOT_EMPTY) {
157                inPicture.dataLen -= (u32)(outPicture.pStrmCurrPos - inPicture.pStream);
158                inPicture.pStream = outPicture.pStrmCurrPos;
159                if (ret == H264SWDEC_HDRS_RDY_BUFF_NOT_EMPTY) {
160                    mHeadersDecoded = true;
161                    H264SwDecInfo decoderInfo;
162                    CHECK(H264SwDecGetInfo(mHandle, &decoderInfo) == H264SWDEC_OK);
163
164                    if (handlePortSettingChangeEvent(&decoderInfo)) {
165                        portSettingsChanged = true;
166                    }
167
168                    if (decoderInfo.croppingFlag &&
169                        handleCropRectEvent(&decoderInfo.cropParams)) {
170                        portSettingsChanged = true;
171                    }
172                }
173            } else {
174                if (portSettingsChanged) {
175                    if (H264SwDecNextPicture(mHandle, &decodedPicture, 0)
176                        == H264SWDEC_PIC_RDY) {
177
178                        // Save this output buffer; otherwise, it will be
179                        // lost during dynamic port reconfiguration because
180                        // OpenMAX client will delete _all_ output buffers
181                        // in the process.
182                        saveFirstOutputBuffer(
183                            decodedPicture.picId,
184                            (uint8_t *)decodedPicture.pOutputPicture);
185                    }
186                }
187                inPicture.dataLen = 0;
188                if (ret < 0) {
189                    ALOGE("Decoder failed: %d", ret);
190
191                    notify(OMX_EventError, OMX_ErrorUndefined,
192                           ERROR_MALFORMED, NULL);
193
194                    mSignalledError = true;
195                    return;
196                }
197            }
198        }
199        inInfo->mOwnedByUs = false;
200        notifyEmptyBufferDone(inHeader);
201
202        if (portSettingsChanged) {
203            portSettingsChanged = false;
204            return;
205        }
206
207        if (mFirstPicture && !outQueue.empty()) {
208            if (!drainOneOutputBuffer(mFirstPictureId, mFirstPicture)) {
209                ALOGE("Drain failed");
210                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
211                mSignalledError = true;
212                return;
213            }
214            delete[] mFirstPicture;
215            mFirstPicture = NULL;
216            mFirstPictureId = -1;
217        }
218
219        drainAllOutputBuffers(false /* eos */);
220    }
221}
222
223bool SoftAVC::handlePortSettingChangeEvent(const H264SwDecInfo *info) {
224    if (mWidth != info->picWidth || mHeight != info->picHeight) {
225        mWidth  = info->picWidth;
226        mHeight = info->picHeight;
227        mPictureSize = mWidth * mHeight * 3 / 2;
228        updatePortDefinitions();
229        notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
230        mOutputPortSettingsChange = AWAITING_DISABLED;
231        return true;
232    }
233
234    return false;
235}
236
237bool SoftAVC::handleCropRectEvent(const CropParams *crop) {
238    if (mCropLeft != crop->cropLeftOffset ||
239        mCropTop != crop->cropTopOffset ||
240        mCropWidth != crop->cropOutWidth ||
241        mCropHeight != crop->cropOutHeight) {
242        mCropLeft = crop->cropLeftOffset;
243        mCropTop = crop->cropTopOffset;
244        mCropWidth = crop->cropOutWidth;
245        mCropHeight = crop->cropOutHeight;
246
247        notify(OMX_EventPortSettingsChanged, 1,
248                OMX_IndexConfigCommonOutputCrop, NULL);
249
250        return true;
251    }
252    return false;
253}
254
255void SoftAVC::saveFirstOutputBuffer(int32_t picId, uint8_t *data) {
256    CHECK(mFirstPicture == NULL);
257    mFirstPictureId = picId;
258
259    mFirstPicture = new uint8_t[mPictureSize];
260    memcpy(mFirstPicture, data, mPictureSize);
261}
262
263bool SoftAVC::drainOneOutputBuffer(int32_t picId, uint8_t* data) {
264    List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
265    BufferInfo *outInfo = *outQueue.begin();
266    OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
267    if (outHeader->nAllocLen - outHeader->nOffset < mPictureSize) {
268        android_errorWriteLog(0x534e4554, "27833616");
269        return false;
270    }
271    outQueue.erase(outQueue.begin());
272    OMX_BUFFERHEADERTYPE *header = mPicToHeaderMap.valueFor(picId);
273    outHeader->nTimeStamp = header->nTimeStamp;
274    outHeader->nFlags = header->nFlags;
275    outHeader->nFilledLen = mPictureSize;
276    memcpy(outHeader->pBuffer + outHeader->nOffset,
277            data, mPictureSize);
278    mPicToHeaderMap.removeItem(picId);
279    delete header;
280    outInfo->mOwnedByUs = false;
281    notifyFillBufferDone(outHeader);
282    return true;
283}
284
285void SoftAVC::drainAllOutputBuffers(bool eos) {
286    List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
287    H264SwDecPicture decodedPicture;
288
289    if (mHeadersDecoded) {
290        while (!outQueue.empty()
291                && H264SWDEC_PIC_RDY == H264SwDecNextPicture(
292                    mHandle, &decodedPicture, eos /* flush */)) {
293            int32_t picId = decodedPicture.picId;
294            uint8_t *data = (uint8_t *) decodedPicture.pOutputPicture;
295            if (!drainOneOutputBuffer(picId, data)) {
296                ALOGE("Drain failed");
297                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
298                mSignalledError = true;
299                return;
300            }
301        }
302    }
303
304    if (!eos) {
305        return;
306    }
307
308    while (!outQueue.empty()) {
309        BufferInfo *outInfo = *outQueue.begin();
310        outQueue.erase(outQueue.begin());
311        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
312
313        outHeader->nTimeStamp = 0;
314        outHeader->nFilledLen = 0;
315        outHeader->nFlags = OMX_BUFFERFLAG_EOS;
316
317        outInfo->mOwnedByUs = false;
318        notifyFillBufferDone(outHeader);
319
320        mEOSStatus = OUTPUT_FRAMES_FLUSHED;
321    }
322}
323
324void SoftAVC::onPortFlushCompleted(OMX_U32 portIndex) {
325    if (portIndex == kInputPortIndex) {
326        mEOSStatus = INPUT_DATA_AVAILABLE;
327    }
328}
329
330void SoftAVC::onReset() {
331    SoftVideoDecoderOMXComponent::onReset();
332    mSignalledError = false;
333}
334
335}  // namespace android
336
337android::SoftOMXComponent *createSoftOMXComponent(
338        const char *name, const OMX_CALLBACKTYPE *callbacks,
339        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
340    return new android::SoftAVC(name, callbacks, appData, component);
341}
342