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 mCtx(NULL), 42 mImg(NULL) { 43 // arbitrary from avc/hevc as vpx does not specify a min compression ratio 44 const size_t kMinCompressionRatio = mMode == MODE_VP8 ? 2 : 4; 45 const char *mime = mMode == MODE_VP8 ? MEDIA_MIMETYPE_VIDEO_VP8 : MEDIA_MIMETYPE_VIDEO_VP9; 46 const size_t kMaxOutputBufferSize = 2048 * 2048 * 3 / 2; 47 initPorts( 48 kNumBuffers, kMaxOutputBufferSize / kMinCompressionRatio /* inputBufferSize */, 49 kNumBuffers, mime, kMinCompressionRatio); 50 CHECK_EQ(initDecoder(), (status_t)OK); 51} 52 53SoftVPX::~SoftVPX() { 54 vpx_codec_destroy((vpx_codec_ctx_t *)mCtx); 55 delete (vpx_codec_ctx_t *)mCtx; 56 mCtx = NULL; 57} 58 59static int GetCPUCoreCount() { 60 int cpuCoreCount = 1; 61#if defined(_SC_NPROCESSORS_ONLN) 62 cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN); 63#else 64 // _SC_NPROC_ONLN must be defined... 65 cpuCoreCount = sysconf(_SC_NPROC_ONLN); 66#endif 67 CHECK(cpuCoreCount >= 1); 68 ALOGV("Number of CPU cores: %d", cpuCoreCount); 69 return cpuCoreCount; 70} 71 72status_t SoftVPX::initDecoder() { 73 mCtx = new vpx_codec_ctx_t; 74 vpx_codec_err_t vpx_err; 75 vpx_codec_dec_cfg_t cfg; 76 memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t)); 77 cfg.threads = GetCPUCoreCount(); 78 if ((vpx_err = vpx_codec_dec_init( 79 (vpx_codec_ctx_t *)mCtx, 80 mMode == MODE_VP8 ? &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo, 81 &cfg, 0))) { 82 ALOGE("on2 decoder failed to initialize. (%d)", vpx_err); 83 return UNKNOWN_ERROR; 84 } 85 86 return OK; 87} 88 89void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) { 90 if (mOutputPortSettingsChange != NONE) { 91 return; 92 } 93 94 List<BufferInfo *> &inQueue = getPortQueue(0); 95 List<BufferInfo *> &outQueue = getPortQueue(1); 96 bool EOSseen = false; 97 98 while (!inQueue.empty() && !outQueue.empty()) { 99 BufferInfo *inInfo = *inQueue.begin(); 100 OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; 101 102 BufferInfo *outInfo = *outQueue.begin(); 103 OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; 104 105 if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { 106 EOSseen = true; 107 if (inHeader->nFilledLen == 0) { 108 inQueue.erase(inQueue.begin()); 109 inInfo->mOwnedByUs = false; 110 notifyEmptyBufferDone(inHeader); 111 112 outHeader->nFilledLen = 0; 113 outHeader->nFlags = OMX_BUFFERFLAG_EOS; 114 115 outQueue.erase(outQueue.begin()); 116 outInfo->mOwnedByUs = false; 117 notifyFillBufferDone(outHeader); 118 return; 119 } 120 } 121 122 if (mImg == NULL) { 123 if (vpx_codec_decode( 124 (vpx_codec_ctx_t *)mCtx, 125 inHeader->pBuffer + inHeader->nOffset, 126 inHeader->nFilledLen, 127 NULL, 128 0)) { 129 ALOGE("on2 decoder failed to decode frame."); 130 131 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 132 return; 133 } 134 vpx_codec_iter_t iter = NULL; 135 mImg = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); 136 } 137 138 if (mImg != NULL) { 139 CHECK_EQ(mImg->fmt, IMG_FMT_I420); 140 141 uint32_t width = mImg->d_w; 142 uint32_t height = mImg->d_h; 143 bool portWillReset = false; 144 handlePortSettingsChange(&portWillReset, width, height); 145 if (portWillReset) { 146 return; 147 } 148 149 outHeader->nOffset = 0; 150 outHeader->nFilledLen = (width * height * 3) / 2; 151 outHeader->nFlags = EOSseen ? OMX_BUFFERFLAG_EOS : 0; 152 outHeader->nTimeStamp = inHeader->nTimeStamp; 153 154 uint8_t *dst = outHeader->pBuffer; 155 const uint8_t *srcY = (const uint8_t *)mImg->planes[PLANE_Y]; 156 const uint8_t *srcU = (const uint8_t *)mImg->planes[PLANE_U]; 157 const uint8_t *srcV = (const uint8_t *)mImg->planes[PLANE_V]; 158 size_t srcYStride = mImg->stride[PLANE_Y]; 159 size_t srcUStride = mImg->stride[PLANE_U]; 160 size_t srcVStride = mImg->stride[PLANE_V]; 161 copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride); 162 163 mImg = NULL; 164 outInfo->mOwnedByUs = false; 165 outQueue.erase(outQueue.begin()); 166 outInfo = NULL; 167 notifyFillBufferDone(outHeader); 168 outHeader = NULL; 169 } 170 171 inInfo->mOwnedByUs = false; 172 inQueue.erase(inQueue.begin()); 173 inInfo = NULL; 174 notifyEmptyBufferDone(inHeader); 175 inHeader = NULL; 176 } 177} 178 179} // namespace android 180 181android::SoftOMXComponent *createSoftOMXComponent( 182 const char *name, const OMX_CALLBACKTYPE *callbacks, 183 OMX_PTR appData, OMX_COMPONENTTYPE **component) { 184 if (!strcmp(name, "OMX.google.vp8.decoder")) { 185 return new android::SoftVPX( 186 name, "video_decoder.vp8", OMX_VIDEO_CodingVP8, 187 callbacks, appData, component); 188 } else if (!strcmp(name, "OMX.google.vp9.decoder")) { 189 return new android::SoftVPX( 190 name, "video_decoder.vp9", OMX_VIDEO_CodingVP9, 191 callbacks, appData, component); 192 } else { 193 CHECK(!"Unknown component"); 194 } 195 return NULL; 196} 197