SoftVPX.cpp revision d411b4ca2945cd8974a3a78199fce94646950128
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, 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 = (width * height * 3) / 2;
155            outHeader->nFlags = EOSseen ? OMX_BUFFERFLAG_EOS : 0;
156            outHeader->nTimeStamp = inHeader->nTimeStamp;
157
158            const uint8_t *srcLine = (const uint8_t *)img->planes[PLANE_Y];
159            uint8_t *dst = outHeader->pBuffer;
160            for (size_t i = 0; i < img->d_h; ++i) {
161                memcpy(dst, srcLine, img->d_w);
162
163                srcLine += img->stride[PLANE_Y];
164                dst += img->d_w;
165            }
166
167            srcLine = (const uint8_t *)img->planes[PLANE_U];
168            for (size_t i = 0; i < img->d_h / 2; ++i) {
169                memcpy(dst, srcLine, img->d_w / 2);
170
171                srcLine += img->stride[PLANE_U];
172                dst += img->d_w / 2;
173            }
174
175            srcLine = (const uint8_t *)img->planes[PLANE_V];
176            for (size_t i = 0; i < img->d_h / 2; ++i) {
177                memcpy(dst, srcLine, img->d_w / 2);
178
179                srcLine += img->stride[PLANE_V];
180                dst += img->d_w / 2;
181            }
182
183            outInfo->mOwnedByUs = false;
184            outQueue.erase(outQueue.begin());
185            outInfo = NULL;
186            notifyFillBufferDone(outHeader);
187            outHeader = NULL;
188        }
189
190        inInfo->mOwnedByUs = false;
191        inQueue.erase(inQueue.begin());
192        inInfo = NULL;
193        notifyEmptyBufferDone(inHeader);
194        inHeader = NULL;
195    }
196}
197
198}  // namespace android
199
200android::SoftOMXComponent *createSoftOMXComponent(
201        const char *name, const OMX_CALLBACKTYPE *callbacks,
202        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
203    if (!strcmp(name, "OMX.google.vp8.decoder")) {
204        return new android::SoftVPX(
205                name, "video_decoder.vp8", OMX_VIDEO_CodingVP8,
206                callbacks, appData, component);
207    } else if (!strcmp(name, "OMX.google.vp9.decoder")) {
208        return new android::SoftVPX(
209                name, "video_decoder.vp9", OMX_VIDEO_CodingVP9,
210                callbacks, appData, component);
211    } else {
212        CHECK(!"Unknown component");
213    }
214}
215