SoftVPX.cpp revision a02eae5e911f3bdc3f84f39c0ef223261b646128
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 "SoftVPX"
19#include <utils/Log.h>
20
21#include "SoftVPX.h"
22
23#include <media/stagefright/foundation/ADebug.h>
24#include <media/stagefright/MediaDefs.h>
25
26#include "vpx/vpx_decoder.h"
27#include "vpx/vpx_codec.h"
28#include "vpx/vp8dx.h"
29
30namespace android {
31
32template<class T>
33static void InitOMXParams(T *params) {
34    params->nSize = sizeof(T);
35    params->nVersion.s.nVersionMajor = 1;
36    params->nVersion.s.nVersionMinor = 0;
37    params->nVersion.s.nRevision = 0;
38    params->nVersion.s.nStep = 0;
39}
40
41SoftVPX::SoftVPX(
42        const char *name,
43        const OMX_CALLBACKTYPE *callbacks,
44        OMX_PTR appData,
45        OMX_COMPONENTTYPE **component)
46    : SimpleSoftOMXComponent(name, callbacks, appData, component),
47      mCtx(NULL),
48      mWidth(320),
49      mHeight(240),
50      mOutputPortSettingsChange(NONE) {
51    initPorts();
52    CHECK_EQ(initDecoder(), (status_t)OK);
53}
54
55SoftVPX::~SoftVPX() {
56    vpx_codec_destroy((vpx_codec_ctx_t *)mCtx);
57    delete (vpx_codec_ctx_t *)mCtx;
58    mCtx = NULL;
59}
60
61void SoftVPX::initPorts() {
62    OMX_PARAM_PORTDEFINITIONTYPE def;
63    InitOMXParams(&def);
64
65    def.nPortIndex = 0;
66    def.eDir = OMX_DirInput;
67    def.nBufferCountMin = kNumBuffers;
68    def.nBufferCountActual = def.nBufferCountMin;
69    def.nBufferSize = 768 * 1024;
70    def.bEnabled = OMX_TRUE;
71    def.bPopulated = OMX_FALSE;
72    def.eDomain = OMX_PortDomainVideo;
73    def.bBuffersContiguous = OMX_FALSE;
74    def.nBufferAlignment = 1;
75
76    def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_VPX);
77    def.format.video.pNativeRender = NULL;
78    def.format.video.nFrameWidth = mWidth;
79    def.format.video.nFrameHeight = mHeight;
80    def.format.video.nStride = def.format.video.nFrameWidth;
81    def.format.video.nSliceHeight = def.format.video.nFrameHeight;
82    def.format.video.nBitrate = 0;
83    def.format.video.xFramerate = 0;
84    def.format.video.bFlagErrorConcealment = OMX_FALSE;
85    def.format.video.eCompressionFormat = OMX_VIDEO_CodingVPX;
86    def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
87    def.format.video.pNativeWindow = NULL;
88
89    addPort(def);
90
91    def.nPortIndex = 1;
92    def.eDir = OMX_DirOutput;
93    def.nBufferCountMin = kNumBuffers;
94    def.nBufferCountActual = def.nBufferCountMin;
95    def.bEnabled = OMX_TRUE;
96    def.bPopulated = OMX_FALSE;
97    def.eDomain = OMX_PortDomainVideo;
98    def.bBuffersContiguous = OMX_FALSE;
99    def.nBufferAlignment = 2;
100
101    def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW);
102    def.format.video.pNativeRender = NULL;
103    def.format.video.nFrameWidth = mWidth;
104    def.format.video.nFrameHeight = mHeight;
105    def.format.video.nStride = def.format.video.nFrameWidth;
106    def.format.video.nSliceHeight = def.format.video.nFrameHeight;
107    def.format.video.nBitrate = 0;
108    def.format.video.xFramerate = 0;
109    def.format.video.bFlagErrorConcealment = OMX_FALSE;
110    def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
111    def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
112    def.format.video.pNativeWindow = NULL;
113
114    def.nBufferSize =
115        (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2;
116
117    addPort(def);
118}
119
120static int GetCPUCoreCount() {
121    int cpuCoreCount = 1;
122#if defined(_SC_NPROCESSORS_ONLN)
123    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
124#else
125    // _SC_NPROC_ONLN must be defined...
126    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
127#endif
128    CHECK(cpuCoreCount >= 1);
129    ALOGV("Number of CPU cores: %d", cpuCoreCount);
130    return cpuCoreCount;
131}
132
133status_t SoftVPX::initDecoder() {
134    mCtx = new vpx_codec_ctx_t;
135    vpx_codec_err_t vpx_err;
136    vpx_codec_dec_cfg_t cfg;
137    memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t));
138    cfg.threads = GetCPUCoreCount();
139    if ((vpx_err = vpx_codec_dec_init(
140                (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, &cfg, 0))) {
141        ALOGE("on2 decoder failed to initialize. (%d)", vpx_err);
142        return UNKNOWN_ERROR;
143    }
144
145    return OK;
146}
147
148OMX_ERRORTYPE SoftVPX::internalGetParameter(
149        OMX_INDEXTYPE index, OMX_PTR params) {
150    switch (index) {
151        case OMX_IndexParamVideoPortFormat:
152        {
153            OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
154                (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
155
156            if (formatParams->nPortIndex > 1) {
157                return OMX_ErrorUndefined;
158            }
159
160            if (formatParams->nIndex != 0) {
161                return OMX_ErrorNoMore;
162            }
163
164            if (formatParams->nPortIndex == 0) {
165                formatParams->eCompressionFormat = OMX_VIDEO_CodingVPX;
166                formatParams->eColorFormat = OMX_COLOR_FormatUnused;
167                formatParams->xFramerate = 0;
168            } else {
169                CHECK_EQ(formatParams->nPortIndex, 1u);
170
171                formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
172                formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
173                formatParams->xFramerate = 0;
174            }
175
176            return OMX_ErrorNone;
177        }
178
179        default:
180            return SimpleSoftOMXComponent::internalGetParameter(index, params);
181    }
182}
183
184OMX_ERRORTYPE SoftVPX::internalSetParameter(
185        OMX_INDEXTYPE index, const OMX_PTR params) {
186    switch (index) {
187        case OMX_IndexParamStandardComponentRole:
188        {
189            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
190                (const OMX_PARAM_COMPONENTROLETYPE *)params;
191
192            if (strncmp((const char *)roleParams->cRole,
193                        "video_decoder.vpx",
194                        OMX_MAX_STRINGNAME_SIZE - 1)) {
195                return OMX_ErrorUndefined;
196            }
197
198            return OMX_ErrorNone;
199        }
200
201        case OMX_IndexParamVideoPortFormat:
202        {
203            OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
204                (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
205
206            if (formatParams->nPortIndex > 1) {
207                return OMX_ErrorUndefined;
208            }
209
210            if (formatParams->nIndex != 0) {
211                return OMX_ErrorNoMore;
212            }
213
214            return OMX_ErrorNone;
215        }
216
217        default:
218            return SimpleSoftOMXComponent::internalSetParameter(index, params);
219    }
220}
221
222void SoftVPX::onQueueFilled(OMX_U32 portIndex) {
223    if (mOutputPortSettingsChange != NONE) {
224        return;
225    }
226
227    List<BufferInfo *> &inQueue = getPortQueue(0);
228    List<BufferInfo *> &outQueue = getPortQueue(1);
229    bool EOSseen = false;
230
231    while (!inQueue.empty() && !outQueue.empty()) {
232        BufferInfo *inInfo = *inQueue.begin();
233        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
234
235        BufferInfo *outInfo = *outQueue.begin();
236        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
237
238        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
239            EOSseen = true;
240            if (inHeader->nFilledLen == 0) {
241                inQueue.erase(inQueue.begin());
242                inInfo->mOwnedByUs = false;
243                notifyEmptyBufferDone(inHeader);
244
245                outHeader->nFilledLen = 0;
246                outHeader->nFlags = OMX_BUFFERFLAG_EOS;
247
248                outQueue.erase(outQueue.begin());
249                outInfo->mOwnedByUs = false;
250                notifyFillBufferDone(outHeader);
251                return;
252            }
253        }
254
255        if (vpx_codec_decode(
256                    (vpx_codec_ctx_t *)mCtx,
257                    inHeader->pBuffer + inHeader->nOffset,
258                    inHeader->nFilledLen,
259                    NULL,
260                    0)) {
261            ALOGE("on2 decoder failed to decode frame.");
262
263            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
264            return;
265        }
266
267        vpx_codec_iter_t iter = NULL;
268        vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter);
269
270        if (img != NULL) {
271            CHECK_EQ(img->fmt, IMG_FMT_I420);
272
273            int32_t width = img->d_w;
274            int32_t height = img->d_h;
275
276            if (width != mWidth || height != mHeight) {
277                mWidth = width;
278                mHeight = height;
279
280                updatePortDefinitions();
281
282                notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
283                mOutputPortSettingsChange = AWAITING_DISABLED;
284                return;
285            }
286
287            outHeader->nOffset = 0;
288            outHeader->nFilledLen = (width * height * 3) / 2;
289            outHeader->nFlags = EOSseen ? OMX_BUFFERFLAG_EOS : 0;
290            outHeader->nTimeStamp = inHeader->nTimeStamp;
291
292            const uint8_t *srcLine = (const uint8_t *)img->planes[PLANE_Y];
293            uint8_t *dst = outHeader->pBuffer;
294            for (size_t i = 0; i < img->d_h; ++i) {
295                memcpy(dst, srcLine, img->d_w);
296
297                srcLine += img->stride[PLANE_Y];
298                dst += img->d_w;
299            }
300
301            srcLine = (const uint8_t *)img->planes[PLANE_U];
302            for (size_t i = 0; i < img->d_h / 2; ++i) {
303                memcpy(dst, srcLine, img->d_w / 2);
304
305                srcLine += img->stride[PLANE_U];
306                dst += img->d_w / 2;
307            }
308
309            srcLine = (const uint8_t *)img->planes[PLANE_V];
310            for (size_t i = 0; i < img->d_h / 2; ++i) {
311                memcpy(dst, srcLine, img->d_w / 2);
312
313                srcLine += img->stride[PLANE_V];
314                dst += img->d_w / 2;
315            }
316
317            outInfo->mOwnedByUs = false;
318            outQueue.erase(outQueue.begin());
319            outInfo = NULL;
320            notifyFillBufferDone(outHeader);
321            outHeader = NULL;
322        }
323
324        inInfo->mOwnedByUs = false;
325        inQueue.erase(inQueue.begin());
326        inInfo = NULL;
327        notifyEmptyBufferDone(inHeader);
328        inHeader = NULL;
329    }
330}
331
332void SoftVPX::onPortFlushCompleted(OMX_U32 portIndex) {
333}
334
335void SoftVPX::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
336    if (portIndex != 1) {
337        return;
338    }
339
340    switch (mOutputPortSettingsChange) {
341        case NONE:
342            break;
343
344        case AWAITING_DISABLED:
345        {
346            CHECK(!enabled);
347            mOutputPortSettingsChange = AWAITING_ENABLED;
348            break;
349        }
350
351        default:
352        {
353            CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
354            CHECK(enabled);
355            mOutputPortSettingsChange = NONE;
356            break;
357        }
358    }
359}
360
361void SoftVPX::updatePortDefinitions() {
362    OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef;
363    def->format.video.nFrameWidth = mWidth;
364    def->format.video.nFrameHeight = mHeight;
365    def->format.video.nStride = def->format.video.nFrameWidth;
366    def->format.video.nSliceHeight = def->format.video.nFrameHeight;
367
368    def = &editPortInfo(1)->mDef;
369    def->format.video.nFrameWidth = mWidth;
370    def->format.video.nFrameHeight = mHeight;
371    def->format.video.nStride = def->format.video.nFrameWidth;
372    def->format.video.nSliceHeight = def->format.video.nFrameHeight;
373
374    def->nBufferSize =
375        (def->format.video.nFrameWidth
376            * def->format.video.nFrameHeight * 3) / 2;
377}
378
379}  // namespace android
380
381android::SoftOMXComponent *createSoftOMXComponent(
382        const char *name, const OMX_CALLBACKTYPE *callbacks,
383        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
384    return new android::SoftVPX(name, callbacks, appData, component);
385}
386
387