SoftVPX.cpp revision 5a25d9382cc96f56c6f178c11313eb26ad7000cc
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 (outHeader->nAllocLen >= outHeader->nFilledLen) { 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 ALOGE("b/27597103, buffer too small"); 170 android_errorWriteLog(0x534e4554, "27597103"); 171 outHeader->nFilledLen = 0; 172 } 173 174 mImg = NULL; 175 outInfo->mOwnedByUs = false; 176 outQueue.erase(outQueue.begin()); 177 outInfo = NULL; 178 notifyFillBufferDone(outHeader); 179 outHeader = NULL; 180 } 181 182 if (!eos) { 183 return true; 184 } 185 186 if (!outQueue.empty()) { 187 outInfo = *outQueue.begin(); 188 outQueue.erase(outQueue.begin()); 189 outHeader = outInfo->mHeader; 190 outHeader->nTimeStamp = 0; 191 outHeader->nFilledLen = 0; 192 outHeader->nFlags = OMX_BUFFERFLAG_EOS; 193 outInfo->mOwnedByUs = false; 194 notifyFillBufferDone(outHeader); 195 mEOSStatus = OUTPUT_FRAMES_FLUSHED; 196 } 197 return true; 198} 199 200void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) { 201 if (mOutputPortSettingsChange != NONE || mEOSStatus == OUTPUT_FRAMES_FLUSHED) { 202 return; 203 } 204 205 List<BufferInfo *> &inQueue = getPortQueue(0); 206 List<BufferInfo *> &outQueue = getPortQueue(1); 207 bool EOSseen = false; 208 bool portWillReset = false; 209 210 while ((mEOSStatus == INPUT_EOS_SEEN || !inQueue.empty()) 211 && !outQueue.empty()) { 212 // Output the pending frames that left from last port reset or decoder flush. 213 if (mEOSStatus == INPUT_EOS_SEEN || mImg != NULL) { 214 if (!outputBuffers( 215 mEOSStatus == INPUT_EOS_SEEN, true /* display */, 216 mEOSStatus == INPUT_EOS_SEEN, &portWillReset)) { 217 ALOGE("on2 decoder failed to output frame."); 218 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 219 return; 220 } 221 if (portWillReset || mEOSStatus == OUTPUT_FRAMES_FLUSHED || 222 mEOSStatus == INPUT_EOS_SEEN) { 223 return; 224 } 225 // Continue as outQueue may be empty now. 226 continue; 227 } 228 229 BufferInfo *inInfo = *inQueue.begin(); 230 OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; 231 232 // Software VP9 Decoder does not need the Codec Specific Data (CSD) 233 // (specified in http://www.webmproject.org/vp9/profiles/). Ignore it if 234 // it was passed. 235 if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { 236 // Only ignore CSD buffer for VP9. 237 if (mMode == MODE_VP9) { 238 inQueue.erase(inQueue.begin()); 239 inInfo->mOwnedByUs = false; 240 notifyEmptyBufferDone(inHeader); 241 continue; 242 } else { 243 // Tolerate the CSD buffer for VP8. This is a workaround 244 // for b/28689536. 245 ALOGW("WARNING: Got CSD buffer for VP8."); 246 } 247 } 248 249 mTimeStamps[mTimeStampIdx] = inHeader->nTimeStamp; 250 251 if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { 252 mEOSStatus = INPUT_EOS_SEEN; 253 EOSseen = true; 254 } 255 256 if (inHeader->nFilledLen > 0) { 257 vpx_codec_err_t err = vpx_codec_decode( 258 (vpx_codec_ctx_t *)mCtx, inHeader->pBuffer + inHeader->nOffset, 259 inHeader->nFilledLen, &mTimeStamps[mTimeStampIdx], 0); 260 if (err == VPX_CODEC_OK) { 261 inInfo->mOwnedByUs = false; 262 inQueue.erase(inQueue.begin()); 263 inInfo = NULL; 264 notifyEmptyBufferDone(inHeader); 265 inHeader = NULL; 266 } else { 267 ALOGE("on2 decoder failed to decode frame. err: %d", err); 268 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 269 return; 270 } 271 } 272 273 mTimeStampIdx = (mTimeStampIdx + 1) % kNumBuffers; 274 275 if (!outputBuffers( 276 EOSseen /* flushDecoder */, true /* display */, EOSseen, &portWillReset)) { 277 ALOGE("on2 decoder failed to output frame."); 278 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 279 return; 280 } 281 if (portWillReset) { 282 return; 283 } 284 } 285} 286 287void SoftVPX::onPortFlushCompleted(OMX_U32 portIndex) { 288 if (portIndex == kInputPortIndex) { 289 bool portWillReset = false; 290 if (!outputBuffers( 291 true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) { 292 ALOGE("Failed to flush decoder."); 293 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 294 return; 295 } 296 mEOSStatus = INPUT_DATA_AVAILABLE; 297 } 298} 299 300void SoftVPX::onReset() { 301 bool portWillReset = false; 302 if (!outputBuffers( 303 true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) { 304 ALOGW("Failed to flush decoder. Try to hard reset decoder"); 305 destroyDecoder(); 306 initDecoder(); 307 } 308 mEOSStatus = INPUT_DATA_AVAILABLE; 309} 310 311} // namespace android 312 313android::SoftOMXComponent *createSoftOMXComponent( 314 const char *name, const OMX_CALLBACKTYPE *callbacks, 315 OMX_PTR appData, OMX_COMPONENTTYPE **component) { 316 if (!strcmp(name, "OMX.google.vp8.decoder")) { 317 return new android::SoftVPX( 318 name, "video_decoder.vp8", OMX_VIDEO_CodingVP8, 319 callbacks, appData, component); 320 } else if (!strcmp(name, "OMX.google.vp9.decoder")) { 321 return new android::SoftVPX( 322 name, "video_decoder.vp9", OMX_VIDEO_CodingVP9, 323 callbacks, appData, component); 324 } else { 325 CHECK(!"Unknown component"); 326 } 327 return NULL; 328} 329