SoftVPX.cpp revision 3c23af85baa6e248681ca98f857c4af84b5ebffc
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 21#include "SoftVPX.h" 22 23#include <media/stagefright/foundation/ADebug.h> 24#include <media/stagefright/MediaDefs.h> 25 26 27namespace android { 28 29SoftVPX::SoftVPX( 30 const char *name, 31 const char *componentRole, 32 OMX_VIDEO_CODINGTYPE codingType, 33 const OMX_CALLBACKTYPE *callbacks, 34 OMX_PTR appData, 35 OMX_COMPONENTTYPE **component) 36 : SoftVideoDecoderOMXComponent( 37 name, componentRole, codingType, 38 NULL /* profileLevels */, 0 /* numProfileLevels */, 39 320 /* width */, 240 /* height */, callbacks, appData, component), 40 mMode(codingType == OMX_VIDEO_CodingVP8 ? MODE_VP8 : MODE_VP9), 41 mEOSStatus(INPUT_DATA_AVAILABLE), 42 mCtx(NULL), 43 mFrameParallelMode(false), 44 mTimeStampIdx(0), 45 mImg(NULL) { 46 // arbitrary from avc/hevc as vpx does not specify a min compression ratio 47 const size_t kMinCompressionRatio = mMode == MODE_VP8 ? 2 : 4; 48 const char *mime = mMode == MODE_VP8 ? MEDIA_MIMETYPE_VIDEO_VP8 : MEDIA_MIMETYPE_VIDEO_VP9; 49 const size_t kMaxOutputBufferSize = 2048 * 2048 * 3 / 2; 50 initPorts( 51 kNumBuffers, kMaxOutputBufferSize / kMinCompressionRatio /* inputBufferSize */, 52 kNumBuffers, mime, kMinCompressionRatio); 53 CHECK_EQ(initDecoder(), (status_t)OK); 54} 55 56SoftVPX::~SoftVPX() { 57 destroyDecoder(); 58} 59 60static int GetCPUCoreCount() { 61 int cpuCoreCount = 1; 62#if defined(_SC_NPROCESSORS_ONLN) 63 cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN); 64#else 65 // _SC_NPROC_ONLN must be defined... 66 cpuCoreCount = sysconf(_SC_NPROC_ONLN); 67#endif 68 CHECK(cpuCoreCount >= 1); 69 ALOGV("Number of CPU cores: %d", cpuCoreCount); 70 return cpuCoreCount; 71} 72 73status_t SoftVPX::initDecoder() { 74 mCtx = new vpx_codec_ctx_t; 75 vpx_codec_err_t vpx_err; 76 vpx_codec_dec_cfg_t cfg; 77 vpx_codec_flags_t flags; 78 memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t)); 79 memset(&flags, 0, sizeof(vpx_codec_flags_t)); 80 cfg.threads = GetCPUCoreCount(); 81 82 if (mFrameParallelMode) { 83 flags |= VPX_CODEC_USE_FRAME_THREADING; 84 } 85 86 if ((vpx_err = vpx_codec_dec_init( 87 (vpx_codec_ctx_t *)mCtx, 88 mMode == MODE_VP8 ? &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo, 89 &cfg, flags))) { 90 ALOGE("on2 decoder failed to initialize. (%d)", vpx_err); 91 return UNKNOWN_ERROR; 92 } 93 94 return OK; 95} 96 97status_t SoftVPX::destroyDecoder() { 98 vpx_codec_destroy((vpx_codec_ctx_t *)mCtx); 99 delete (vpx_codec_ctx_t *)mCtx; 100 mCtx = NULL; 101 return OK; 102} 103 104bool SoftVPX::outputBuffers(bool flushDecoder, bool display, bool eos, bool *portWillReset) { 105 List<BufferInfo *> &outQueue = getPortQueue(1); 106 BufferInfo *outInfo = NULL; 107 OMX_BUFFERHEADERTYPE *outHeader = NULL; 108 vpx_codec_iter_t iter = NULL; 109 110 if (flushDecoder && mFrameParallelMode) { 111 // Flush decoder by passing NULL data ptr and 0 size. 112 // Ideally, this should never fail. 113 if (vpx_codec_decode((vpx_codec_ctx_t *)mCtx, NULL, 0, NULL, 0)) { 114 ALOGE("Failed to flush on2 decoder."); 115 return false; 116 } 117 } 118 119 if (!display) { 120 if (!flushDecoder) { 121 ALOGE("Invalid operation."); 122 return false; 123 } 124 // Drop all the decoded frames in decoder. 125 while ((mImg = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter))) { 126 } 127 return true; 128 } 129 130 while (!outQueue.empty()) { 131 if (mImg == NULL) { 132 mImg = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); 133 if (mImg == NULL) { 134 break; 135 } 136 } 137 uint32_t width = mImg->d_w; 138 uint32_t height = mImg->d_h; 139 outInfo = *outQueue.begin(); 140 outHeader = outInfo->mHeader; 141 CHECK_EQ(mImg->fmt, VPX_IMG_FMT_I420); 142 handlePortSettingsChange(portWillReset, width, height); 143 if (*portWillReset) { 144 return true; 145 } 146 147 outHeader->nOffset = 0; 148 outHeader->nFlags = 0; 149 outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * 3) / 2; 150 outHeader->nTimeStamp = *(OMX_TICKS *)mImg->user_priv; 151 152 uint8_t *dst = outHeader->pBuffer; 153 const uint8_t *srcY = (const uint8_t *)mImg->planes[VPX_PLANE_Y]; 154 const uint8_t *srcU = (const uint8_t *)mImg->planes[VPX_PLANE_U]; 155 const uint8_t *srcV = (const uint8_t *)mImg->planes[VPX_PLANE_V]; 156 size_t srcYStride = mImg->stride[VPX_PLANE_Y]; 157 size_t srcUStride = mImg->stride[VPX_PLANE_U]; 158 size_t srcVStride = mImg->stride[VPX_PLANE_V]; 159 copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride); 160 161 mImg = NULL; 162 outInfo->mOwnedByUs = false; 163 outQueue.erase(outQueue.begin()); 164 outInfo = NULL; 165 notifyFillBufferDone(outHeader); 166 outHeader = NULL; 167 } 168 169 if (!eos) { 170 return true; 171 } 172 173 if (!outQueue.empty()) { 174 outInfo = *outQueue.begin(); 175 outQueue.erase(outQueue.begin()); 176 outHeader = outInfo->mHeader; 177 outHeader->nTimeStamp = 0; 178 outHeader->nFilledLen = 0; 179 outHeader->nFlags = OMX_BUFFERFLAG_EOS; 180 outInfo->mOwnedByUs = false; 181 notifyFillBufferDone(outHeader); 182 mEOSStatus = OUTPUT_FRAMES_FLUSHED; 183 } 184 return true; 185} 186 187void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) { 188 if (mOutputPortSettingsChange != NONE || mEOSStatus == OUTPUT_FRAMES_FLUSHED) { 189 return; 190 } 191 192 List<BufferInfo *> &inQueue = getPortQueue(0); 193 List<BufferInfo *> &outQueue = getPortQueue(1); 194 bool EOSseen = false; 195 bool portWillReset = false; 196 197 while ((mEOSStatus == INPUT_EOS_SEEN || !inQueue.empty()) 198 && !outQueue.empty()) { 199 // Output the pending frames that left from last port reset or decoder flush. 200 if (mEOSStatus == INPUT_EOS_SEEN || mImg != NULL) { 201 if (!outputBuffers( 202 mEOSStatus == INPUT_EOS_SEEN, true /* display */, 203 mEOSStatus == INPUT_EOS_SEEN, &portWillReset)) { 204 ALOGE("on2 decoder failed to output frame."); 205 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 206 return; 207 } 208 if (portWillReset || mEOSStatus == OUTPUT_FRAMES_FLUSHED || 209 mEOSStatus == INPUT_EOS_SEEN) { 210 return; 211 } 212 } 213 214 BufferInfo *inInfo = *inQueue.begin(); 215 OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; 216 mTimeStamps[mTimeStampIdx] = inHeader->nTimeStamp; 217 218 if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { 219 mEOSStatus = INPUT_EOS_SEEN; 220 EOSseen = true; 221 } 222 223 if (inHeader->nFilledLen > 0 && 224 vpx_codec_decode((vpx_codec_ctx_t *)mCtx, 225 inHeader->pBuffer + inHeader->nOffset, 226 inHeader->nFilledLen, 227 &mTimeStamps[mTimeStampIdx], 0)) { 228 ALOGE("on2 decoder failed to decode frame."); 229 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 230 return; 231 } 232 mTimeStampIdx = (mTimeStampIdx + 1) % kNumBuffers; 233 234 if (!outputBuffers( 235 EOSseen /* flushDecoder */, true /* display */, EOSseen, &portWillReset)) { 236 ALOGE("on2 decoder failed to output frame."); 237 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 238 return; 239 } 240 if (portWillReset) { 241 return; 242 } 243 244 inInfo->mOwnedByUs = false; 245 inQueue.erase(inQueue.begin()); 246 inInfo = NULL; 247 notifyEmptyBufferDone(inHeader); 248 inHeader = NULL; 249 } 250} 251 252void SoftVPX::onPortFlushCompleted(OMX_U32 portIndex) { 253 if (portIndex == kInputPortIndex) { 254 bool portWillReset = false; 255 if (!outputBuffers( 256 true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) { 257 ALOGE("Failed to flush decoder."); 258 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 259 return; 260 } 261 mEOSStatus = INPUT_DATA_AVAILABLE; 262 } 263} 264 265void SoftVPX::onReset() { 266 bool portWillReset = false; 267 if (!outputBuffers( 268 true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) { 269 ALOGW("Failed to flush decoder. Try to hard reset decoder"); 270 destroyDecoder(); 271 initDecoder(); 272 } 273 mEOSStatus = INPUT_DATA_AVAILABLE; 274} 275 276} // namespace android 277 278android::SoftOMXComponent *createSoftOMXComponent( 279 const char *name, const OMX_CALLBACKTYPE *callbacks, 280 OMX_PTR appData, OMX_COMPONENTTYPE **component) { 281 if (!strcmp(name, "OMX.google.vp8.decoder")) { 282 return new android::SoftVPX( 283 name, "video_decoder.vp8", OMX_VIDEO_CodingVP8, 284 callbacks, appData, component); 285 } else if (!strcmp(name, "OMX.google.vp9.decoder")) { 286 return new android::SoftVPX( 287 name, "video_decoder.vp9", OMX_VIDEO_CodingVP9, 288 callbacks, appData, component); 289 } else { 290 CHECK(!"Unknown component"); 291 } 292 return NULL; 293} 294