SoftVPX.cpp revision 1595f8ac7ab412e81e0565e1347edaeedf9f2832
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
32SoftVPX::SoftVPX(
33        const char *name,
34        const char *componentRole,
35        OMX_VIDEO_CODINGTYPE codingType,
36        const OMX_CALLBACKTYPE *callbacks,
37        OMX_PTR appData,
38        OMX_COMPONENTTYPE **component)
39    : SoftVideoDecoderOMXComponent(
40            name, componentRole, codingType,
41            NULL /* profileLevels */, 0 /* numProfileLevels */,
42            320 /* width */, 240 /* height */, callbacks, appData, component),
43      mMode(codingType == OMX_VIDEO_CodingVP8 ? MODE_VP8 : MODE_VP9),
44      mCtx(NULL) {
45    initPorts(kNumBuffers, 768 * 1024 /* inputBufferSize */,
46            kNumBuffers,
47            codingType == OMX_VIDEO_CodingVP8 ? MEDIA_MIMETYPE_VIDEO_VP8 : MEDIA_MIMETYPE_VIDEO_VP9);
48
49    CHECK_EQ(initDecoder(), (status_t)OK);
50}
51
52SoftVPX::~SoftVPX() {
53    vpx_codec_destroy((vpx_codec_ctx_t *)mCtx);
54    delete (vpx_codec_ctx_t *)mCtx;
55    mCtx = NULL;
56}
57
58static int GetCPUCoreCount() {
59    int cpuCoreCount = 1;
60#if defined(_SC_NPROCESSORS_ONLN)
61    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
62#else
63    // _SC_NPROC_ONLN must be defined...
64    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
65#endif
66    CHECK(cpuCoreCount >= 1);
67    ALOGV("Number of CPU cores: %d", cpuCoreCount);
68    return cpuCoreCount;
69}
70
71status_t SoftVPX::initDecoder() {
72    mCtx = new vpx_codec_ctx_t;
73    vpx_codec_err_t vpx_err;
74    vpx_codec_dec_cfg_t cfg;
75    memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t));
76    cfg.threads = GetCPUCoreCount();
77    if ((vpx_err = vpx_codec_dec_init(
78                (vpx_codec_ctx_t *)mCtx,
79                 mMode == MODE_VP8 ? &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo,
80                 &cfg, 0))) {
81        ALOGE("on2 decoder failed to initialize. (%d)", vpx_err);
82        return UNKNOWN_ERROR;
83    }
84
85    return OK;
86}
87
88void SoftVPX::onQueueFilled(OMX_U32 portIndex) {
89    if (mOutputPortSettingsChange != NONE) {
90        return;
91    }
92
93    List<BufferInfo *> &inQueue = getPortQueue(0);
94    List<BufferInfo *> &outQueue = getPortQueue(1);
95    bool EOSseen = false;
96
97    while (!inQueue.empty() && !outQueue.empty()) {
98        BufferInfo *inInfo = *inQueue.begin();
99        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
100
101        BufferInfo *outInfo = *outQueue.begin();
102        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
103
104        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
105            EOSseen = true;
106            if (inHeader->nFilledLen == 0) {
107                inQueue.erase(inQueue.begin());
108                inInfo->mOwnedByUs = false;
109                notifyEmptyBufferDone(inHeader);
110
111                outHeader->nFilledLen = 0;
112                outHeader->nFlags = OMX_BUFFERFLAG_EOS;
113
114                outQueue.erase(outQueue.begin());
115                outInfo->mOwnedByUs = false;
116                notifyFillBufferDone(outHeader);
117                return;
118            }
119        }
120
121        if (vpx_codec_decode(
122                    (vpx_codec_ctx_t *)mCtx,
123                    inHeader->pBuffer + inHeader->nOffset,
124                    inHeader->nFilledLen,
125                    NULL,
126                    0)) {
127            ALOGE("on2 decoder failed to decode frame.");
128
129            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
130            return;
131        }
132
133        vpx_codec_iter_t iter = NULL;
134        vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter);
135
136        if (img != NULL) {
137            CHECK_EQ(img->fmt, VPX_IMG_FMT_I420);
138
139            uint32_t width = img->d_w;
140            uint32_t height = img->d_h;
141
142            if (width != mWidth || height != mHeight) {
143                mWidth = width;
144                mHeight = height;
145
146                updatePortDefinitions();
147
148                notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
149                mOutputPortSettingsChange = AWAITING_DISABLED;
150                return;
151            }
152
153            outHeader->nOffset = 0;
154            outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
155            outHeader->nFlags = EOSseen ? OMX_BUFFERFLAG_EOS : 0;
156            outHeader->nTimeStamp = inHeader->nTimeStamp;
157
158            if (outHeader->nAllocLen >= outHeader->nFilledLen) {
159                const uint8_t *srcLine = (const uint8_t *)img->planes[VPX_PLANE_Y];
160                uint8_t *dst = outHeader->pBuffer;
161                for (size_t i = 0; i < img->d_h; ++i) {
162                    memcpy(dst, srcLine, img->d_w);
163
164                    srcLine += img->stride[VPX_PLANE_Y];
165                    dst += img->d_w;
166                }
167
168                srcLine = (const uint8_t *)img->planes[VPX_PLANE_U];
169                for (size_t i = 0; i < img->d_h / 2; ++i) {
170                    memcpy(dst, srcLine, img->d_w / 2);
171
172                    srcLine += img->stride[VPX_PLANE_U];
173                    dst += img->d_w / 2;
174                }
175
176                srcLine = (const uint8_t *)img->planes[VPX_PLANE_V];
177                for (size_t i = 0; i < img->d_h / 2; ++i) {
178                    memcpy(dst, srcLine, img->d_w / 2);
179
180                    srcLine += img->stride[VPX_PLANE_V];
181                    dst += img->d_w / 2;
182                }
183            } else {
184                ALOGE("b/27597103, buffer too small");
185                android_errorWriteLog(0x534e4554, "27597103");
186                outHeader->nFilledLen = 0;
187            }
188            outInfo->mOwnedByUs = false;
189            outQueue.erase(outQueue.begin());
190            outInfo = NULL;
191            notifyFillBufferDone(outHeader);
192            outHeader = NULL;
193        }
194
195        inInfo->mOwnedByUs = false;
196        inQueue.erase(inQueue.begin());
197        inInfo = NULL;
198        notifyEmptyBufferDone(inHeader);
199        inHeader = NULL;
200    }
201}
202
203}  // namespace android
204
205android::SoftOMXComponent *createSoftOMXComponent(
206        const char *name, const OMX_CALLBACKTYPE *callbacks,
207        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
208    if (!strcmp(name, "OMX.google.vp8.decoder")) {
209        return new android::SoftVPX(
210                name, "video_decoder.vp8", OMX_VIDEO_CodingVP8,
211                callbacks, appData, component);
212    } else if (!strcmp(name, "OMX.google.vp9.decoder")) {
213        return new android::SoftVPX(
214                name, "video_decoder.vp9", OMX_VIDEO_CodingVP9,
215                callbacks, appData, component);
216    } else {
217        CHECK(!"Unknown component");
218    }
219}
220