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