SoftVPX.cpp revision 9486e0a16e9ad4d4f6bd5047a3cbb1b3f2008d65
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 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 169 mImg = NULL; 170 outInfo->mOwnedByUs = false; 171 outQueue.erase(outQueue.begin()); 172 outInfo = NULL; 173 notifyFillBufferDone(outHeader); 174 outHeader = NULL; 175 } 176 177 if (!eos) { 178 return true; 179 } 180 181 if (!outQueue.empty()) { 182 outInfo = *outQueue.begin(); 183 outQueue.erase(outQueue.begin()); 184 outHeader = outInfo->mHeader; 185 outHeader->nTimeStamp = 0; 186 outHeader->nFilledLen = 0; 187 outHeader->nFlags = OMX_BUFFERFLAG_EOS; 188 outInfo->mOwnedByUs = false; 189 notifyFillBufferDone(outHeader); 190 mEOSStatus = OUTPUT_FRAMES_FLUSHED; 191 } 192 return true; 193} 194 195void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) { 196 if (mOutputPortSettingsChange != NONE || mEOSStatus == OUTPUT_FRAMES_FLUSHED) { 197 return; 198 } 199 200 List<BufferInfo *> &inQueue = getPortQueue(0); 201 List<BufferInfo *> &outQueue = getPortQueue(1); 202 bool EOSseen = false; 203 bool portWillReset = false; 204 205 while ((mEOSStatus == INPUT_EOS_SEEN || !inQueue.empty()) 206 && !outQueue.empty()) { 207 // Output the pending frames that left from last port reset or decoder flush. 208 if (mEOSStatus == INPUT_EOS_SEEN || mImg != NULL) { 209 if (!outputBuffers( 210 mEOSStatus == INPUT_EOS_SEEN, true /* display */, 211 mEOSStatus == INPUT_EOS_SEEN, &portWillReset)) { 212 ALOGE("on2 decoder failed to output frame."); 213 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 214 return; 215 } 216 if (portWillReset || mEOSStatus == OUTPUT_FRAMES_FLUSHED || 217 mEOSStatus == INPUT_EOS_SEEN) { 218 return; 219 } 220 // Continue as outQueue may be empty now. 221 continue; 222 } 223 224 BufferInfo *inInfo = *inQueue.begin(); 225 OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; 226 mTimeStamps[mTimeStampIdx] = inHeader->nTimeStamp; 227 228 if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { 229 mEOSStatus = INPUT_EOS_SEEN; 230 EOSseen = true; 231 } 232 233 if (inHeader->nFilledLen > 0) { 234 vpx_codec_err_t err = vpx_codec_decode( 235 (vpx_codec_ctx_t *)mCtx, inHeader->pBuffer + inHeader->nOffset, 236 inHeader->nFilledLen, &mTimeStamps[mTimeStampIdx], 0); 237 if (err == VPX_CODEC_OK) { 238 inInfo->mOwnedByUs = false; 239 inQueue.erase(inQueue.begin()); 240 inInfo = NULL; 241 notifyEmptyBufferDone(inHeader); 242 inHeader = NULL; 243 } else { 244 ALOGE("on2 decoder failed to decode frame."); 245 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 246 return; 247 } 248 } 249 250 mTimeStampIdx = (mTimeStampIdx + 1) % kNumBuffers; 251 252 if (!outputBuffers( 253 EOSseen /* flushDecoder */, true /* display */, EOSseen, &portWillReset)) { 254 ALOGE("on2 decoder failed to output frame."); 255 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 256 return; 257 } 258 if (portWillReset) { 259 return; 260 } 261 } 262} 263 264void SoftVPX::onPortFlushCompleted(OMX_U32 portIndex) { 265 if (portIndex == kInputPortIndex) { 266 bool portWillReset = false; 267 if (!outputBuffers( 268 true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) { 269 ALOGE("Failed to flush decoder."); 270 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 271 return; 272 } 273 mEOSStatus = INPUT_DATA_AVAILABLE; 274 } 275} 276 277void SoftVPX::onReset() { 278 bool portWillReset = false; 279 if (!outputBuffers( 280 true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) { 281 ALOGW("Failed to flush decoder. Try to hard reset decoder"); 282 destroyDecoder(); 283 initDecoder(); 284 } 285 mEOSStatus = INPUT_DATA_AVAILABLE; 286} 287 288} // namespace android 289 290android::SoftOMXComponent *createSoftOMXComponent( 291 const char *name, const OMX_CALLBACKTYPE *callbacks, 292 OMX_PTR appData, OMX_COMPONENTTYPE **component) { 293 if (!strcmp(name, "OMX.google.vp8.decoder")) { 294 return new android::SoftVPX( 295 name, "video_decoder.vp8", OMX_VIDEO_CodingVP8, 296 callbacks, appData, component); 297 } else if (!strcmp(name, "OMX.google.vp9.decoder")) { 298 return new android::SoftVPX( 299 name, "video_decoder.vp9", OMX_VIDEO_CodingVP9, 300 callbacks, appData, component); 301 } else { 302 CHECK(!"Unknown component"); 303 } 304 return NULL; 305} 306