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 #include <utils/Log.h>
19 #include <utils/misc.h>
20 //#include "OMX_VideoExt.h"
21
22 #define DEBUG 0
23 #if DEBUG
24 #define DDD(...) ALOGD(__VA_ARGS__)
25 #else
26 #define DDD(...) ((void)0)
27 #endif
28
29 #include "GoldfishVPX.h"
30
31 #include <media/stagefright/foundation/ADebug.h>
32 #include <media/stagefright/MediaDefs.h>
33
34 #include <OMX_VideoExt.h>
35 #include <inttypes.h>
36
37 #include <nativebase/nativebase.h>
38
39 #include <android/hardware/graphics/common/1.2/types.h>
40 #include <hidl/LegacySupport.h>
41
42 using ::android::hardware::graphics::common::V1_0::BufferUsage;
43 using ::android::hardware::graphics::common::V1_2::PixelFormat;
44
45 namespace android {
46
47 // Only need to declare the highest supported profile and level here.
48 static const CodecProfileLevel kVP9ProfileLevels[] = {
49 { OMX_VIDEO_VP9Profile0, OMX_VIDEO_VP9Level5 },
50 { OMX_VIDEO_VP9Profile2, OMX_VIDEO_VP9Level5 },
51 { OMX_VIDEO_VP9Profile2HDR, OMX_VIDEO_VP9Level5 },
52 { OMX_VIDEO_VP9Profile2HDR10Plus, OMX_VIDEO_VP9Level5 },
53 };
54
GoldfishVPX(const char * name,const char * componentRole,OMX_VIDEO_CODINGTYPE codingType,const OMX_CALLBACKTYPE * callbacks,OMX_PTR appData,OMX_COMPONENTTYPE ** component,RenderMode renderMode)55 GoldfishVPX::GoldfishVPX(const char* name,
56 const char* componentRole,
57 OMX_VIDEO_CODINGTYPE codingType,
58 const OMX_CALLBACKTYPE* callbacks,
59 OMX_PTR appData,
60 OMX_COMPONENTTYPE** component,
61 RenderMode renderMode)
62 : GoldfishVideoDecoderOMXComponent(
63 name,
64 componentRole,
65 codingType,
66 codingType == OMX_VIDEO_CodingVP8 ? NULL : kVP9ProfileLevels,
67 codingType == OMX_VIDEO_CodingVP8 ? 0 : NELEM(kVP9ProfileLevels),
68 320 /* width */,
69 240 /* height */,
70 callbacks,
71 appData,
72 component),
73 mMode(codingType == OMX_VIDEO_CodingVP8 ? MODE_VP8 : MODE_VP9),
74 mRenderMode(renderMode),
75 mEOSStatus(INPUT_DATA_AVAILABLE),
76 mCtx(NULL),
77 mFrameParallelMode(true),
78 mTimeStampIdx(0),
79 mImg(NULL) {
80 // arbitrary from avc/hevc as vpx does not specify a min compression ratio
81 const size_t kMinCompressionRatio = mMode == MODE_VP8 ? 2 : 4;
82 const char* mime = mMode == MODE_VP8 ? MEDIA_MIMETYPE_VIDEO_VP8
83 : MEDIA_MIMETYPE_VIDEO_VP9;
84 const size_t kMaxOutputBufferSize = 3840 * 2160 * 3 / 2; // 4k
85 initPorts(kNumBuffers,
86 kMaxOutputBufferSize / kMinCompressionRatio /* inputBufferSize */,
87 kNumBuffers, mime, kMinCompressionRatio);
88 ALOGI("calling constructor of GoldfishVPX");
89 // wait till later
90 // CHECK_EQ(initDecoder(), (status_t)OK);
91 }
92
~GoldfishVPX()93 GoldfishVPX::~GoldfishVPX() {
94 ALOGI("calling destructor of GoldfishVPX");
95 destroyDecoder();
96 }
97
supportDescribeHdrStaticInfo()98 bool GoldfishVPX::supportDescribeHdrStaticInfo() {
99 return true;
100 }
101
supportDescribeHdr10PlusInfo()102 bool GoldfishVPX::supportDescribeHdr10PlusInfo() {
103 return true;
104 }
105
initDecoder()106 status_t GoldfishVPX::initDecoder() {
107 mCtx = new vpx_codec_ctx_t;
108 mCtx->vpversion = mMode == MODE_VP8 ? 8 : 9;
109
110 mCtx->version = mEnableAndroidNativeBuffers ? 200 : 100;
111
112 int vpx_err = 0;
113 if ((vpx_err = vpx_codec_dec_init(mCtx))) {
114 ALOGE("vpx decoder failed to initialize. (%d)", vpx_err);
115 delete mCtx;
116 mCtx = NULL;
117 return UNKNOWN_ERROR;
118 }
119
120 ALOGI("calling init GoldfishVPX ctx %p", mCtx);
121 return OK;
122 }
123
destroyDecoder()124 status_t GoldfishVPX::destroyDecoder() {
125 if (mCtx) {
126 ALOGI("calling destroying GoldfishVPX ctx %p", mCtx);
127 vpx_codec_destroy(mCtx);
128 delete mCtx;
129 mCtx = NULL;
130 }
131 return OK;
132 }
133
setup_ctx_parameters(vpx_codec_ctx_t * ctx,int hostColorBufferId)134 void GoldfishVPX::setup_ctx_parameters(vpx_codec_ctx_t* ctx,
135 int hostColorBufferId) {
136 ctx->width = mWidth;
137 ctx->height = mHeight;
138 ctx->hostColorBufferId = hostColorBufferId;
139 ctx->outputBufferWidth = outputBufferWidth();
140 ctx->outputBufferHeight = outputBufferHeight();
141 OMX_PARAM_PORTDEFINITIONTYPE *outDef = &editPortInfo(kOutputPortIndex)->mDef;
142 int32_t bpp = (outDef->format.video.eColorFormat == OMX_COLOR_FormatYUV420Planar16) ? 2 : 1;
143 ctx->bpp = bpp;
144 }
145
outputBuffers(bool flushDecoder,bool display,bool eos,bool * portWillReset)146 bool GoldfishVPX::outputBuffers(bool flushDecoder, bool display, bool eos, bool *portWillReset) {
147 List<BufferInfo *> &outQueue = getPortQueue(1);
148 BufferInfo *outInfo = NULL;
149 OMX_BUFFERHEADERTYPE *outHeader = NULL;
150 DDD("%s %d", __func__, __LINE__);
151
152 if (flushDecoder && mFrameParallelMode) {
153 // Flush decoder by passing NULL data ptr and 0 size.
154 // Ideally, this should never fail.
155 if (vpx_codec_flush(mCtx)) {
156 ALOGE("Failed to flush on2 decoder.");
157 return false;
158 }
159 }
160
161 if (!display) {
162 if (!flushDecoder) {
163 ALOGE("Invalid operation.");
164 return false;
165 }
166 // Drop all the decoded frames in decoder.
167 // TODO: move this to host, with something like
168 // vpx_codec_drop_all_frames(mCtx);
169 setup_ctx_parameters(mCtx);
170 while ((mImg = vpx_codec_get_frame(mCtx))) {
171 }
172 return true;
173 }
174
175 while (!outQueue.empty()) {
176 DDD("%s %d", __func__, __LINE__);
177 outInfo = *outQueue.begin();
178 outHeader = outInfo->mHeader;
179 if (mImg == NULL) {
180 setup_ctx_parameters(mCtx, getHostColorBufferId(outHeader));
181 mImg = vpx_codec_get_frame(mCtx);
182 if (mImg == NULL) {
183 break;
184 }
185 }
186 uint32_t width = mImg->d_w;
187 uint32_t height = mImg->d_h;
188 CHECK(mImg->fmt == VPX_IMG_FMT_I420 || mImg->fmt == VPX_IMG_FMT_I42016);
189 OMX_COLOR_FORMATTYPE outputColorFormat = OMX_COLOR_FormatYUV420Planar;
190 int32_t bpp = 1;
191 if (mImg->fmt == VPX_IMG_FMT_I42016) {
192 outputColorFormat = OMX_COLOR_FormatYUV420Planar16;
193 bpp = 2;
194 }
195 handlePortSettingsChange(portWillReset, width, height, outputColorFormat);
196 if (*portWillReset) {
197 return true;
198 }
199
200 outHeader->nOffset = 0;
201 outHeader->nFlags = 0;
202 outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * bpp * 3) / 2;
203 PrivInfo *privInfo = (PrivInfo *)mImg->user_priv;
204 outHeader->nTimeStamp = privInfo->mTimeStamp;
205 if (privInfo->mHdr10PlusInfo != nullptr) {
206 queueOutputFrameConfig(privInfo->mHdr10PlusInfo);
207 }
208
209 if (outputBufferSafe(outHeader) &&
210 getHostColorBufferId(outHeader) < 0) {
211 uint8_t *dst = outHeader->pBuffer;
212 memcpy(dst, mCtx->dst, outHeader->nFilledLen);
213 } else {
214 // outHeader->nFilledLen = 0;
215 }
216
217 mImg = NULL;
218 outInfo->mOwnedByUs = false;
219 outQueue.erase(outQueue.begin());
220 outInfo = NULL;
221 notifyFillBufferDone(outHeader);
222 outHeader = NULL;
223 }
224
225 if (!eos) {
226 return true;
227 }
228
229 if (!outQueue.empty()) {
230 outInfo = *outQueue.begin();
231 outQueue.erase(outQueue.begin());
232 outHeader = outInfo->mHeader;
233 outHeader->nTimeStamp = 0;
234 outHeader->nFilledLen = 0;
235 outHeader->nFlags = OMX_BUFFERFLAG_EOS;
236 outInfo->mOwnedByUs = false;
237 notifyFillBufferDone(outHeader);
238 mEOSStatus = OUTPUT_FRAMES_FLUSHED;
239 }
240 return true;
241 }
242
outputBufferSafe(OMX_BUFFERHEADERTYPE * outHeader)243 bool GoldfishVPX::outputBufferSafe(OMX_BUFFERHEADERTYPE *outHeader) {
244 DDD("%s %d", __func__, __LINE__);
245 uint32_t width = outputBufferWidth();
246 uint32_t height = outputBufferHeight();
247 uint64_t nFilledLen = width;
248 nFilledLen *= height;
249 if (nFilledLen > UINT32_MAX / 3) {
250 ALOGE("b/29421675, nFilledLen overflow %llu w %u h %u",
251 (unsigned long long)nFilledLen, width, height);
252 android_errorWriteLog(0x534e4554, "29421675");
253 return false;
254 } else if (outHeader->nAllocLen < outHeader->nFilledLen) {
255 ALOGE("b/27597103, buffer too small");
256 android_errorWriteLog(0x534e4554, "27597103");
257 return false;
258 }
259
260 return true;
261 }
262
onQueueFilled(OMX_U32)263 void GoldfishVPX::onQueueFilled(OMX_U32 /* portIndex */) {
264 DDD("%s %d", __func__, __LINE__);
265 if (mOutputPortSettingsChange != NONE || mEOSStatus == OUTPUT_FRAMES_FLUSHED) {
266 return;
267 }
268
269 if (mCtx == nullptr) {
270 if (OK != initDecoder()) {
271 ALOGE("Failed to initialize decoder");
272 notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL);
273 return;
274 }
275 }
276
277 List<BufferInfo *> &inQueue = getPortQueue(0);
278 List<BufferInfo *> &outQueue = getPortQueue(1);
279 bool EOSseen = false;
280 bool portWillReset = false;
281
282 while ((mEOSStatus == INPUT_EOS_SEEN || !inQueue.empty())
283 && !outQueue.empty()) {
284 // Output the pending frames that left from last port reset or decoder flush.
285 if (mEOSStatus == INPUT_EOS_SEEN || mImg != NULL) {
286 if (!outputBuffers(
287 mEOSStatus == INPUT_EOS_SEEN, true /* display */,
288 mEOSStatus == INPUT_EOS_SEEN, &portWillReset)) {
289 ALOGE("on2 decoder failed to output frame.");
290 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
291 return;
292 }
293 if (portWillReset || mEOSStatus == OUTPUT_FRAMES_FLUSHED ||
294 mEOSStatus == INPUT_EOS_SEEN) {
295 return;
296 }
297 // Continue as outQueue may be empty now.
298 continue;
299 }
300
301 BufferInfo *inInfo = *inQueue.begin();
302 OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
303
304 // Software VP9 Decoder does not need the Codec Specific Data (CSD)
305 // (specified in http://www.webmproject.org/vp9/profiles/). Ignore it if
306 // it was passed.
307 if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
308 // Only ignore CSD buffer for VP9.
309 if (mMode == MODE_VP9) {
310 inQueue.erase(inQueue.begin());
311 inInfo->mOwnedByUs = false;
312 notifyEmptyBufferDone(inHeader);
313 continue;
314 } else {
315 // Tolerate the CSD buffer for VP8. This is a workaround
316 // for b/28689536.
317 ALOGW("WARNING: Got CSD buffer for VP8.");
318 }
319 }
320
321 mPrivInfo[mTimeStampIdx].mTimeStamp = inHeader->nTimeStamp;
322
323 if (inInfo->mFrameConfig) {
324 mPrivInfo[mTimeStampIdx].mHdr10PlusInfo = dequeueInputFrameConfig();
325 } else {
326 mPrivInfo[mTimeStampIdx].mHdr10PlusInfo.clear();
327 }
328
329 if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
330 mEOSStatus = INPUT_EOS_SEEN;
331 EOSseen = true;
332 }
333
334 if (inHeader->nFilledLen > 0) {
335 int err = vpx_codec_decode(mCtx, inHeader->pBuffer + inHeader->nOffset,
336 inHeader->nFilledLen, &mPrivInfo[mTimeStampIdx], 0);
337 if (err == VPX_CODEC_OK) {
338 inInfo->mOwnedByUs = false;
339 inQueue.erase(inQueue.begin());
340 inInfo = NULL;
341 notifyEmptyBufferDone(inHeader);
342 inHeader = NULL;
343 } else {
344 ALOGE("on2 decoder failed to decode frame. err: %d", err);
345 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
346 return;
347 }
348 }
349
350 mTimeStampIdx = (mTimeStampIdx + 1) % kNumBuffers;
351
352 if (!outputBuffers(
353 EOSseen /* flushDecoder */, true /* display */, EOSseen, &portWillReset)) {
354 ALOGE("on2 decoder failed to output frame.");
355 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
356 return;
357 }
358 if (portWillReset) {
359 return;
360 }
361 }
362 }
363
onPortFlushCompleted(OMX_U32 portIndex)364 void GoldfishVPX::onPortFlushCompleted(OMX_U32 portIndex) {
365 DDD("%s %d", __func__, __LINE__);
366 if (portIndex == kInputPortIndex) {
367 bool portWillReset = false;
368 if (!outputBuffers(
369 true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) {
370 ALOGE("Failed to flush decoder.");
371 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
372 return;
373 }
374 mEOSStatus = INPUT_DATA_AVAILABLE;
375 }
376 }
377
onReset()378 void GoldfishVPX::onReset() {
379 DDD("%s %d", __func__, __LINE__);
380 bool portWillReset = false;
381 if (!outputBuffers(
382 true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) {
383 ALOGW("Failed to flush decoder. Try to hard reset decoder");
384 destroyDecoder();
385 initDecoder();
386 }
387 mEOSStatus = INPUT_DATA_AVAILABLE;
388 }
389
getHostColorBufferId(void * header)390 int GoldfishVPX::getHostColorBufferId(void* header) {
391 DDD("%s %d", __func__, __LINE__);
392 if (mNWBuffers.find(header) == mNWBuffers.end()) {
393 DDD("cannot find color buffer for header %p", header);
394 return -1;
395 }
396 sp<ANativeWindowBuffer> nBuf = mNWBuffers[header];
397 cb_handle_t* handle = (cb_handle_t*)nBuf->handle;
398 DDD("found color buffer for header %p --> %d", header, handle->hostHandle);
399 return handle->hostHandle;
400 }
401
internalGetParameter(OMX_INDEXTYPE index,OMX_PTR params)402 OMX_ERRORTYPE GoldfishVPX::internalGetParameter(OMX_INDEXTYPE index,
403 OMX_PTR params) {
404 const int32_t indexFull = index;
405 switch (indexFull) {
406 case kGetAndroidNativeBufferUsageIndex: {
407 DDD("calling kGetAndroidNativeBufferUsageIndex");
408 GetAndroidNativeBufferUsageParams* nativeBuffersUsage =
409 (GetAndroidNativeBufferUsageParams*)params;
410 nativeBuffersUsage->nUsage =
411 (unsigned int)(BufferUsage::GPU_DATA_BUFFER);
412 return OMX_ErrorNone;
413 }
414
415 default:
416 return GoldfishVideoDecoderOMXComponent::internalGetParameter(
417 index, params);
418 }
419 }
420
internalSetParameter(OMX_INDEXTYPE index,const OMX_PTR params)421 OMX_ERRORTYPE GoldfishVPX::internalSetParameter(OMX_INDEXTYPE index,
422 const OMX_PTR params) {
423 // Include extension index OMX_INDEXEXTTYPE.
424 const int32_t indexFull = index;
425
426 switch (indexFull) {
427 case kEnableAndroidNativeBuffersIndex: {
428 DDD("calling kEnableAndroidNativeBuffersIndex");
429 EnableAndroidNativeBuffersParams* enableNativeBuffers =
430 (EnableAndroidNativeBuffersParams*)params;
431 if (enableNativeBuffers) {
432 mEnableAndroidNativeBuffers = enableNativeBuffers->enable;
433 if (mEnableAndroidNativeBuffers == false) {
434 mNWBuffers.clear();
435 DDD("disabled kEnableAndroidNativeBuffersIndex");
436 } else {
437 DDD("enabled kEnableAndroidNativeBuffersIndex");
438 }
439 }
440 return OMX_ErrorNone;
441 }
442
443 case kUseAndroidNativeBufferIndex: {
444 if (mEnableAndroidNativeBuffers == false) {
445 ALOGE("Error: not enabled Android Native Buffers");
446 return OMX_ErrorBadParameter;
447 }
448 UseAndroidNativeBufferParams* use_buffer_params =
449 (UseAndroidNativeBufferParams*)params;
450 if (use_buffer_params) {
451 sp<ANativeWindowBuffer> nBuf = use_buffer_params->nativeBuffer;
452 cb_handle_t* handle = (cb_handle_t*)nBuf->handle;
453 void* dst = NULL;
454 DDD("kUseAndroidNativeBufferIndex with handle %p host color "
455 "handle %d "
456 "calling usebuffer",
457 handle, handle->hostHandle);
458 useBufferCallerLockedAlready(use_buffer_params->bufferHeader,
459 use_buffer_params->nPortIndex,
460 use_buffer_params->pAppPrivate,
461 handle->allocatedSize(),
462 (OMX_U8*)dst);
463 mNWBuffers[*(use_buffer_params->bufferHeader)] =
464 use_buffer_params->nativeBuffer;
465 ;
466 }
467 return OMX_ErrorNone;
468 }
469
470 default:
471 return GoldfishVideoDecoderOMXComponent::internalSetParameter(
472 index, params);
473 }
474 }
475
getExtensionIndex(const char * name,OMX_INDEXTYPE * index)476 OMX_ERRORTYPE GoldfishVPX::getExtensionIndex(const char* name,
477 OMX_INDEXTYPE* index) {
478 if (mRenderMode == RenderMode::RENDER_BY_HOST_GPU) {
479 if (!strcmp(name,
480 "OMX.google.android.index.enableAndroidNativeBuffers")) {
481 DDD("calling getExtensionIndex for enable ANB");
482 *(int32_t*)index = kEnableAndroidNativeBuffersIndex;
483 return OMX_ErrorNone;
484 } else if (!strcmp(name,
485 "OMX.google.android.index.useAndroidNativeBuffer")) {
486 *(int32_t*)index = kUseAndroidNativeBufferIndex;
487 return OMX_ErrorNone;
488 } else if (!strcmp(name,
489 "OMX.google.android.index."
490 "getAndroidNativeBufferUsage")) {
491 *(int32_t*)index = kGetAndroidNativeBufferUsageIndex;
492 return OMX_ErrorNone;
493 }
494 }
495 return GoldfishVideoDecoderOMXComponent::getExtensionIndex(name, index);
496 }
497
498 } // namespace android
499
createGoldfishOMXComponent(const char * name,const OMX_CALLBACKTYPE * callbacks,OMX_PTR appData,OMX_COMPONENTTYPE ** component)500 android::GoldfishOMXComponent *createGoldfishOMXComponent(
501 const char *name, const OMX_CALLBACKTYPE *callbacks,
502 OMX_PTR appData, OMX_COMPONENTTYPE **component) {
503 DDD("%s %d", __func__, __LINE__);
504 // only support vp9 to use host hardware decoder, for now
505 if (!strncmp("OMX.android.goldfish.vp9.decoder", name, 32)) {
506 return new android::GoldfishVPX(
507 name, "video_decoder.vp9", OMX_VIDEO_CodingVP9, callbacks,
508 appData, component, RenderMode::RENDER_BY_HOST_GPU);
509 }
510 if (!strncmp("OMX.android.goldfish.vp8.decoder", name, 32)) {
511 return new android::GoldfishVPX(
512 name, "video_decoder.vp8", OMX_VIDEO_CodingVP8, callbacks,
513 appData, component, RenderMode::RENDER_BY_HOST_GPU);
514 }
515 if (!strncmp("OMX.google.goldfish.vp9.decoder", name, 30)) {
516 return new android::GoldfishVPX(
517 name, "video_decoder.vp9", OMX_VIDEO_CodingVP9, callbacks,
518 appData, component, RenderMode::RENDER_BY_GUEST_CPU);
519 }
520 if (!strncmp("OMX.google.goldfish.vp8.decoder", name, 30)) {
521 return new android::GoldfishVPX(
522 name, "video_decoder.vp8", OMX_VIDEO_CodingVP8, callbacks,
523 appData, component, RenderMode::RENDER_BY_GUEST_CPU);
524 }
525 { CHECK(!"Unknown component"); }
526 return NULL;
527 }
528