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#include "vpx/vpx_decoder.h" 27#include "vpx/vpx_codec.h" 28#include "vpx/vp8dx.h" 29 30namespace android { 31 32SoftVPX::SoftVPX( 33 const char *name, 34 const char *componentRole, 35 OMX_VIDEO_CODINGTYPE codingType, 36 const OMX_CALLBACKTYPE *callbacks, 37 OMX_PTR appData, 38 OMX_COMPONENTTYPE **component) 39 : SoftVideoDecoderOMXComponent( 40 name, componentRole, codingType, 41 NULL /* profileLevels */, 0 /* numProfileLevels */, 42 320 /* width */, 240 /* height */, callbacks, appData, component), 43 mMode(codingType == OMX_VIDEO_CodingVP8 ? MODE_VP8 : MODE_VP9), 44 mCtx(NULL) { 45 initPorts(kNumBuffers, 768 * 1024 /* inputBufferSize */, 46 kNumBuffers, 47 codingType == OMX_VIDEO_CodingVP8 ? MEDIA_MIMETYPE_VIDEO_VP8 : MEDIA_MIMETYPE_VIDEO_VP9); 48 49 CHECK_EQ(initDecoder(), (status_t)OK); 50} 51 52SoftVPX::~SoftVPX() { 53 vpx_codec_destroy((vpx_codec_ctx_t *)mCtx); 54 delete (vpx_codec_ctx_t *)mCtx; 55 mCtx = NULL; 56} 57 58static int GetCPUCoreCount() { 59 int cpuCoreCount = 1; 60#if defined(_SC_NPROCESSORS_ONLN) 61 cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN); 62#else 63 // _SC_NPROC_ONLN must be defined... 64 cpuCoreCount = sysconf(_SC_NPROC_ONLN); 65#endif 66 CHECK(cpuCoreCount >= 1); 67 ALOGV("Number of CPU cores: %d", cpuCoreCount); 68 return cpuCoreCount; 69} 70 71status_t SoftVPX::initDecoder() { 72 mCtx = new vpx_codec_ctx_t; 73 vpx_codec_err_t vpx_err; 74 vpx_codec_dec_cfg_t cfg; 75 memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t)); 76 cfg.threads = GetCPUCoreCount(); 77 if ((vpx_err = vpx_codec_dec_init( 78 (vpx_codec_ctx_t *)mCtx, 79 mMode == MODE_VP8 ? &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo, 80 &cfg, 0))) { 81 ALOGE("on2 decoder failed to initialize. (%d)", vpx_err); 82 return UNKNOWN_ERROR; 83 } 84 85 return OK; 86} 87 88void SoftVPX::onQueueFilled(OMX_U32 portIndex) { 89 if (mOutputPortSettingsChange != NONE) { 90 return; 91 } 92 93 List<BufferInfo *> &inQueue = getPortQueue(0); 94 List<BufferInfo *> &outQueue = getPortQueue(1); 95 bool EOSseen = false; 96 97 while (!inQueue.empty() && !outQueue.empty()) { 98 BufferInfo *inInfo = *inQueue.begin(); 99 OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; 100 101 BufferInfo *outInfo = *outQueue.begin(); 102 OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; 103 104 if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { 105 EOSseen = true; 106 if (inHeader->nFilledLen == 0) { 107 inQueue.erase(inQueue.begin()); 108 inInfo->mOwnedByUs = false; 109 notifyEmptyBufferDone(inHeader); 110 111 outHeader->nFilledLen = 0; 112 outHeader->nFlags = OMX_BUFFERFLAG_EOS; 113 114 outQueue.erase(outQueue.begin()); 115 outInfo->mOwnedByUs = false; 116 notifyFillBufferDone(outHeader); 117 return; 118 } 119 } 120 121 if (vpx_codec_decode( 122 (vpx_codec_ctx_t *)mCtx, 123 inHeader->pBuffer + inHeader->nOffset, 124 inHeader->nFilledLen, 125 NULL, 126 0)) { 127 ALOGE("on2 decoder failed to decode frame."); 128 129 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 130 return; 131 } 132 133 vpx_codec_iter_t iter = NULL; 134 vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); 135 136 if (img != NULL) { 137 CHECK_EQ(img->fmt, IMG_FMT_I420); 138 139 uint32_t width = img->d_w; 140 uint32_t height = img->d_h; 141 142 if (width != mWidth || height != mHeight) { 143 mWidth = width; 144 mHeight = height; 145 146 updatePortDefinitions(); 147 148 notify(OMX_EventPortSettingsChanged, 1, 0, NULL); 149 mOutputPortSettingsChange = AWAITING_DISABLED; 150 return; 151 } 152 153 outHeader->nOffset = 0; 154 outHeader->nFilledLen = (width * height * 3) / 2; 155 outHeader->nFlags = EOSseen ? OMX_BUFFERFLAG_EOS : 0; 156 outHeader->nTimeStamp = inHeader->nTimeStamp; 157 158 const uint8_t *srcLine = (const uint8_t *)img->planes[PLANE_Y]; 159 uint8_t *dst = outHeader->pBuffer; 160 for (size_t i = 0; i < img->d_h; ++i) { 161 memcpy(dst, srcLine, img->d_w); 162 163 srcLine += img->stride[PLANE_Y]; 164 dst += img->d_w; 165 } 166 167 srcLine = (const uint8_t *)img->planes[PLANE_U]; 168 for (size_t i = 0; i < img->d_h / 2; ++i) { 169 memcpy(dst, srcLine, img->d_w / 2); 170 171 srcLine += img->stride[PLANE_U]; 172 dst += img->d_w / 2; 173 } 174 175 srcLine = (const uint8_t *)img->planes[PLANE_V]; 176 for (size_t i = 0; i < img->d_h / 2; ++i) { 177 memcpy(dst, srcLine, img->d_w / 2); 178 179 srcLine += img->stride[PLANE_V]; 180 dst += img->d_w / 2; 181 } 182 183 outInfo->mOwnedByUs = false; 184 outQueue.erase(outQueue.begin()); 185 outInfo = NULL; 186 notifyFillBufferDone(outHeader); 187 outHeader = NULL; 188 } 189 190 inInfo->mOwnedByUs = false; 191 inQueue.erase(inQueue.begin()); 192 inInfo = NULL; 193 notifyEmptyBufferDone(inHeader); 194 inHeader = NULL; 195 } 196} 197 198} // namespace android 199 200android::SoftOMXComponent *createSoftOMXComponent( 201 const char *name, const OMX_CALLBACKTYPE *callbacks, 202 OMX_PTR appData, OMX_COMPONENTTYPE **component) { 203 if (!strcmp(name, "OMX.google.vp8.decoder")) { 204 return new android::SoftVPX( 205 name, "video_decoder.vp8", OMX_VIDEO_CodingVP8, 206 callbacks, appData, component); 207 } else if (!strcmp(name, "OMX.google.vp9.decoder")) { 208 return new android::SoftVPX( 209 name, "video_decoder.vp9", OMX_VIDEO_CodingVP9, 210 callbacks, appData, component); 211 } else { 212 CHECK(!"Unknown component"); 213 } 214} 215