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