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 #include <utils/misc.h>
21 #include "OMX_VideoExt.h"
22
23 #include "SoftVPX.h"
24
25 #include <media/stagefright/foundation/ADebug.h>
26 #include <media/stagefright/MediaDefs.h>
27
28
29 namespace android {
30
31 // Only need to declare the highest supported profile and level here.
32 static const CodecProfileLevel kVP9ProfileLevels[] = {
33 { OMX_VIDEO_VP9Profile0, OMX_VIDEO_VP9Level5 },
34 { OMX_VIDEO_VP9Profile2, OMX_VIDEO_VP9Level5 },
35 { OMX_VIDEO_VP9Profile2HDR, OMX_VIDEO_VP9Level5 },
36 { OMX_VIDEO_VP9Profile2HDR10Plus, OMX_VIDEO_VP9Level5 },
37 };
38
SoftVPX(const char * name,const char * componentRole,OMX_VIDEO_CODINGTYPE codingType,const OMX_CALLBACKTYPE * callbacks,OMX_PTR appData,OMX_COMPONENTTYPE ** component)39 SoftVPX::SoftVPX(
40 const char *name,
41 const char *componentRole,
42 OMX_VIDEO_CODINGTYPE codingType,
43 const OMX_CALLBACKTYPE *callbacks,
44 OMX_PTR appData,
45 OMX_COMPONENTTYPE **component)
46 : SoftVideoDecoderOMXComponent(
47 name, componentRole, codingType,
48 codingType == OMX_VIDEO_CodingVP8 ? NULL : kVP9ProfileLevels,
49 codingType == OMX_VIDEO_CodingVP8 ? 0 : NELEM(kVP9ProfileLevels),
50 320 /* width */, 240 /* height */, callbacks, appData, component),
51 mMode(codingType == OMX_VIDEO_CodingVP8 ? MODE_VP8 : MODE_VP9),
52 mEOSStatus(INPUT_DATA_AVAILABLE),
53 mCtx(NULL),
54 mFrameParallelMode(false),
55 mTimeStampIdx(0),
56 mImg(NULL) {
57 // arbitrary from avc/hevc as vpx does not specify a min compression ratio
58 const size_t kMinCompressionRatio = mMode == MODE_VP8 ? 2 : 4;
59 const char *mime = mMode == MODE_VP8 ? MEDIA_MIMETYPE_VIDEO_VP8 : MEDIA_MIMETYPE_VIDEO_VP9;
60 const size_t kMaxOutputBufferSize = 2048 * 2048 * 3 / 2;
61 initPorts(
62 kNumBuffers, kMaxOutputBufferSize / kMinCompressionRatio /* inputBufferSize */,
63 kNumBuffers, mime, kMinCompressionRatio);
64 CHECK_EQ(initDecoder(), (status_t)OK);
65 }
66
~SoftVPX()67 SoftVPX::~SoftVPX() {
68 destroyDecoder();
69 }
70
GetCPUCoreCount()71 static int GetCPUCoreCount() {
72 int cpuCoreCount = 1;
73 #if defined(_SC_NPROCESSORS_ONLN)
74 cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
75 #else
76 // _SC_NPROC_ONLN must be defined...
77 cpuCoreCount = sysconf(_SC_NPROC_ONLN);
78 #endif
79 CHECK(cpuCoreCount >= 1);
80 ALOGV("Number of CPU cores: %d", cpuCoreCount);
81 return cpuCoreCount;
82 }
83
supportDescribeHdrStaticInfo()84 bool SoftVPX::supportDescribeHdrStaticInfo() {
85 return true;
86 }
87
supportDescribeHdr10PlusInfo()88 bool SoftVPX::supportDescribeHdr10PlusInfo() {
89 return true;
90 }
91
initDecoder()92 status_t SoftVPX::initDecoder() {
93 mCtx = new vpx_codec_ctx_t;
94 vpx_codec_err_t vpx_err;
95 vpx_codec_dec_cfg_t cfg;
96 vpx_codec_flags_t flags;
97 memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t));
98 memset(&flags, 0, sizeof(vpx_codec_flags_t));
99 cfg.threads = GetCPUCoreCount();
100
101 if (mFrameParallelMode) {
102 flags |= VPX_CODEC_USE_FRAME_THREADING;
103 }
104
105 if ((vpx_err = vpx_codec_dec_init(
106 (vpx_codec_ctx_t *)mCtx,
107 mMode == MODE_VP8 ? &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo,
108 &cfg, flags))) {
109 ALOGE("on2 decoder failed to initialize. (%d)", vpx_err);
110 return UNKNOWN_ERROR;
111 }
112
113 return OK;
114 }
115
destroyDecoder()116 status_t SoftVPX::destroyDecoder() {
117 vpx_codec_destroy((vpx_codec_ctx_t *)mCtx);
118 delete (vpx_codec_ctx_t *)mCtx;
119 mCtx = NULL;
120 return OK;
121 }
122
outputBuffers(bool flushDecoder,bool display,bool eos,bool * portWillReset)123 bool SoftVPX::outputBuffers(bool flushDecoder, bool display, bool eos, bool *portWillReset) {
124 List<BufferInfo *> &outQueue = getPortQueue(1);
125 BufferInfo *outInfo = NULL;
126 OMX_BUFFERHEADERTYPE *outHeader = NULL;
127 vpx_codec_iter_t iter = NULL;
128
129 if (flushDecoder && mFrameParallelMode) {
130 // Flush decoder by passing NULL data ptr and 0 size.
131 // Ideally, this should never fail.
132 if (vpx_codec_decode((vpx_codec_ctx_t *)mCtx, NULL, 0, NULL, 0)) {
133 ALOGE("Failed to flush on2 decoder.");
134 return false;
135 }
136 }
137
138 if (!display) {
139 if (!flushDecoder) {
140 ALOGE("Invalid operation.");
141 return false;
142 }
143 // Drop all the decoded frames in decoder.
144 while ((mImg = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter))) {
145 }
146 return true;
147 }
148
149 while (!outQueue.empty()) {
150 if (mImg == NULL) {
151 mImg = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter);
152 if (mImg == NULL) {
153 break;
154 }
155 }
156 uint32_t width = mImg->d_w;
157 uint32_t height = mImg->d_h;
158 outInfo = *outQueue.begin();
159 outHeader = outInfo->mHeader;
160 CHECK(mImg->fmt == VPX_IMG_FMT_I420 || mImg->fmt == VPX_IMG_FMT_I42016);
161 OMX_COLOR_FORMATTYPE outputColorFormat = OMX_COLOR_FormatYUV420Planar;
162 int32_t bpp = 1;
163 if (mImg->fmt == VPX_IMG_FMT_I42016) {
164 outputColorFormat = OMX_COLOR_FormatYUV420Planar16;
165 bpp = 2;
166 }
167 handlePortSettingsChange(portWillReset, width, height, outputColorFormat);
168 if (*portWillReset) {
169 return true;
170 }
171
172 outHeader->nOffset = 0;
173 outHeader->nFlags = 0;
174 outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * bpp * 3) / 2;
175 PrivInfo *privInfo = (PrivInfo *)mImg->user_priv;
176 outHeader->nTimeStamp = privInfo->mTimeStamp;
177 if (privInfo->mHdr10PlusInfo != nullptr) {
178 queueOutputFrameConfig(privInfo->mHdr10PlusInfo);
179 }
180
181 if (outputBufferSafe(outHeader)) {
182 uint8_t *dst = outHeader->pBuffer;
183 const uint8_t *srcY = (const uint8_t *)mImg->planes[VPX_PLANE_Y];
184 const uint8_t *srcU = (const uint8_t *)mImg->planes[VPX_PLANE_U];
185 const uint8_t *srcV = (const uint8_t *)mImg->planes[VPX_PLANE_V];
186 size_t srcYStride = mImg->stride[VPX_PLANE_Y];
187 size_t srcUStride = mImg->stride[VPX_PLANE_U];
188 size_t srcVStride = mImg->stride[VPX_PLANE_V];
189 copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride);
190 } else {
191 outHeader->nFilledLen = 0;
192 }
193
194 mImg = NULL;
195 outInfo->mOwnedByUs = false;
196 outQueue.erase(outQueue.begin());
197 outInfo = NULL;
198 notifyFillBufferDone(outHeader);
199 outHeader = NULL;
200 }
201
202 if (!eos) {
203 return true;
204 }
205
206 if (!outQueue.empty()) {
207 outInfo = *outQueue.begin();
208 outQueue.erase(outQueue.begin());
209 outHeader = outInfo->mHeader;
210 outHeader->nTimeStamp = 0;
211 outHeader->nFilledLen = 0;
212 outHeader->nFlags = OMX_BUFFERFLAG_EOS;
213 outInfo->mOwnedByUs = false;
214 notifyFillBufferDone(outHeader);
215 mEOSStatus = OUTPUT_FRAMES_FLUSHED;
216 }
217 return true;
218 }
219
outputBufferSafe(OMX_BUFFERHEADERTYPE * outHeader)220 bool SoftVPX::outputBufferSafe(OMX_BUFFERHEADERTYPE *outHeader) {
221 uint32_t width = outputBufferWidth();
222 uint32_t height = outputBufferHeight();
223 uint64_t nFilledLen = width;
224 nFilledLen *= height;
225 if (nFilledLen > UINT32_MAX / 3) {
226 ALOGE("b/29421675, nFilledLen overflow %llu w %u h %u",
227 (unsigned long long)nFilledLen, width, height);
228 android_errorWriteLog(0x534e4554, "29421675");
229 return false;
230 } else if (outHeader->nAllocLen < outHeader->nFilledLen) {
231 ALOGE("b/27597103, buffer too small");
232 android_errorWriteLog(0x534e4554, "27597103");
233 return false;
234 }
235
236 return true;
237 }
238
onQueueFilled(OMX_U32)239 void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) {
240 if (mOutputPortSettingsChange != NONE || mEOSStatus == OUTPUT_FRAMES_FLUSHED) {
241 return;
242 }
243
244 List<BufferInfo *> &inQueue = getPortQueue(0);
245 List<BufferInfo *> &outQueue = getPortQueue(1);
246 bool EOSseen = false;
247 bool portWillReset = false;
248
249 while ((mEOSStatus == INPUT_EOS_SEEN || !inQueue.empty())
250 && !outQueue.empty()) {
251 // Output the pending frames that left from last port reset or decoder flush.
252 if (mEOSStatus == INPUT_EOS_SEEN || mImg != NULL) {
253 if (!outputBuffers(
254 mEOSStatus == INPUT_EOS_SEEN, true /* display */,
255 mEOSStatus == INPUT_EOS_SEEN, &portWillReset)) {
256 ALOGE("on2 decoder failed to output frame.");
257 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
258 return;
259 }
260 if (portWillReset || mEOSStatus == OUTPUT_FRAMES_FLUSHED ||
261 mEOSStatus == INPUT_EOS_SEEN) {
262 return;
263 }
264 // Continue as outQueue may be empty now.
265 continue;
266 }
267
268 BufferInfo *inInfo = *inQueue.begin();
269 OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
270
271 // Software VP9 Decoder does not need the Codec Specific Data (CSD)
272 // (specified in http://www.webmproject.org/vp9/profiles/). Ignore it if
273 // it was passed.
274 if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
275 // Only ignore CSD buffer for VP9.
276 if (mMode == MODE_VP9) {
277 inQueue.erase(inQueue.begin());
278 inInfo->mOwnedByUs = false;
279 notifyEmptyBufferDone(inHeader);
280 continue;
281 } else {
282 // Tolerate the CSD buffer for VP8. This is a workaround
283 // for b/28689536.
284 ALOGW("WARNING: Got CSD buffer for VP8.");
285 }
286 }
287
288 mPrivInfo[mTimeStampIdx].mTimeStamp = inHeader->nTimeStamp;
289
290 if (inInfo->mFrameConfig) {
291 mPrivInfo[mTimeStampIdx].mHdr10PlusInfo = dequeueInputFrameConfig();
292 } else {
293 mPrivInfo[mTimeStampIdx].mHdr10PlusInfo.clear();
294 }
295
296 if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
297 mEOSStatus = INPUT_EOS_SEEN;
298 EOSseen = true;
299 }
300
301 if (inHeader->nFilledLen > 0) {
302 vpx_codec_err_t err = vpx_codec_decode(
303 (vpx_codec_ctx_t *)mCtx, inHeader->pBuffer + inHeader->nOffset,
304 inHeader->nFilledLen, &mPrivInfo[mTimeStampIdx], 0);
305 if (err == VPX_CODEC_OK) {
306 inInfo->mOwnedByUs = false;
307 inQueue.erase(inQueue.begin());
308 inInfo = NULL;
309 notifyEmptyBufferDone(inHeader);
310 inHeader = NULL;
311 } else {
312 ALOGE("on2 decoder failed to decode frame. err: %d", err);
313 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
314 return;
315 }
316 }
317
318 mTimeStampIdx = (mTimeStampIdx + 1) % kNumBuffers;
319
320 if (!outputBuffers(
321 EOSseen /* flushDecoder */, true /* display */, EOSseen, &portWillReset)) {
322 ALOGE("on2 decoder failed to output frame.");
323 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
324 return;
325 }
326 if (portWillReset) {
327 return;
328 }
329 }
330 }
331
onPortFlushCompleted(OMX_U32 portIndex)332 void SoftVPX::onPortFlushCompleted(OMX_U32 portIndex) {
333 if (portIndex == kInputPortIndex) {
334 bool portWillReset = false;
335 if (!outputBuffers(
336 true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) {
337 ALOGE("Failed to flush decoder.");
338 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
339 return;
340 }
341 mEOSStatus = INPUT_DATA_AVAILABLE;
342 }
343 }
344
onReset()345 void SoftVPX::onReset() {
346 bool portWillReset = false;
347 if (!outputBuffers(
348 true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) {
349 ALOGW("Failed to flush decoder. Try to hard reset decoder");
350 destroyDecoder();
351 initDecoder();
352 }
353 mEOSStatus = INPUT_DATA_AVAILABLE;
354 }
355
356 } // namespace android
357
358 __attribute__((cfi_canonical_jump_table))
createSoftOMXComponent(const char * name,const OMX_CALLBACKTYPE * callbacks,OMX_PTR appData,OMX_COMPONENTTYPE ** component)359 android::SoftOMXComponent *createSoftOMXComponent(
360 const char *name, const OMX_CALLBACKTYPE *callbacks,
361 OMX_PTR appData, OMX_COMPONENTTYPE **component) {
362 if (!strcmp(name, "OMX.google.vp8.decoder")) {
363 return new android::SoftVPX(
364 name, "video_decoder.vp8", OMX_VIDEO_CodingVP8,
365 callbacks, appData, component);
366 } else if (!strcmp(name, "OMX.google.vp9.decoder")) {
367 return new android::SoftVPX(
368 name, "video_decoder.vp9", OMX_VIDEO_CodingVP9,
369 callbacks, appData, component);
370 } else {
371 CHECK(!"Unknown component");
372 }
373 return NULL;
374 }
375