SoftVPX.cpp revision d94e716af0e49d775f0c0c4f36dd2c136ba5f2b2
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 32template<class T> 33static void InitOMXParams(T *params) { 34 params->nSize = sizeof(T); 35 params->nVersion.s.nVersionMajor = 1; 36 params->nVersion.s.nVersionMinor = 0; 37 params->nVersion.s.nRevision = 0; 38 params->nVersion.s.nStep = 0; 39} 40 41SoftVPX::SoftVPX( 42 const char *name, 43 const OMX_CALLBACKTYPE *callbacks, 44 OMX_PTR appData, 45 OMX_COMPONENTTYPE **component) 46 : SimpleSoftOMXComponent(name, callbacks, appData, component), 47 mCtx(NULL), 48 mWidth(320), 49 mHeight(240), 50 mOutputPortSettingsChange(NONE) { 51 initPorts(); 52 CHECK_EQ(initDecoder(), (status_t)OK); 53} 54 55SoftVPX::~SoftVPX() { 56 vpx_codec_destroy((vpx_codec_ctx_t *)mCtx); 57 delete (vpx_codec_ctx_t *)mCtx; 58 mCtx = NULL; 59} 60 61void SoftVPX::initPorts() { 62 OMX_PARAM_PORTDEFINITIONTYPE def; 63 InitOMXParams(&def); 64 65 def.nPortIndex = 0; 66 def.eDir = OMX_DirInput; 67 def.nBufferCountMin = kNumBuffers; 68 def.nBufferCountActual = def.nBufferCountMin; 69 def.nBufferSize = 768 * 1024; 70 def.bEnabled = OMX_TRUE; 71 def.bPopulated = OMX_FALSE; 72 def.eDomain = OMX_PortDomainVideo; 73 def.bBuffersContiguous = OMX_FALSE; 74 def.nBufferAlignment = 1; 75 76 def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_VPX); 77 def.format.video.pNativeRender = NULL; 78 def.format.video.nFrameWidth = mWidth; 79 def.format.video.nFrameHeight = mHeight; 80 def.format.video.nStride = def.format.video.nFrameWidth; 81 def.format.video.nSliceHeight = def.format.video.nFrameHeight; 82 def.format.video.nBitrate = 0; 83 def.format.video.xFramerate = 0; 84 def.format.video.bFlagErrorConcealment = OMX_FALSE; 85 def.format.video.eCompressionFormat = OMX_VIDEO_CodingVPX; 86 def.format.video.eColorFormat = OMX_COLOR_FormatUnused; 87 def.format.video.pNativeWindow = NULL; 88 89 addPort(def); 90 91 def.nPortIndex = 1; 92 def.eDir = OMX_DirOutput; 93 def.nBufferCountMin = kNumBuffers; 94 def.nBufferCountActual = def.nBufferCountMin; 95 def.bEnabled = OMX_TRUE; 96 def.bPopulated = OMX_FALSE; 97 def.eDomain = OMX_PortDomainVideo; 98 def.bBuffersContiguous = OMX_FALSE; 99 def.nBufferAlignment = 2; 100 101 def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW); 102 def.format.video.pNativeRender = NULL; 103 def.format.video.nFrameWidth = mWidth; 104 def.format.video.nFrameHeight = mHeight; 105 def.format.video.nStride = def.format.video.nFrameWidth; 106 def.format.video.nSliceHeight = def.format.video.nFrameHeight; 107 def.format.video.nBitrate = 0; 108 def.format.video.xFramerate = 0; 109 def.format.video.bFlagErrorConcealment = OMX_FALSE; 110 def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; 111 def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; 112 def.format.video.pNativeWindow = NULL; 113 114 def.nBufferSize = 115 (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2; 116 117 addPort(def); 118} 119 120static int GetCPUCoreCount() { 121 int cpuCoreCount = 1; 122#if defined(_SC_NPROCESSORS_ONLN) 123 cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN); 124#else 125 // _SC_NPROC_ONLN must be defined... 126 cpuCoreCount = sysconf(_SC_NPROC_ONLN); 127#endif 128 CHECK(cpuCoreCount >= 1); 129 ALOGV("Number of CPU cores: %d", cpuCoreCount); 130 return cpuCoreCount; 131} 132 133status_t SoftVPX::initDecoder() { 134 mCtx = new vpx_codec_ctx_t; 135 vpx_codec_err_t vpx_err; 136 vpx_codec_dec_cfg_t cfg; 137 memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t)); 138 cfg.threads = GetCPUCoreCount(); 139 if ((vpx_err = vpx_codec_dec_init( 140 (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, &cfg, 0))) { 141 ALOGE("on2 decoder failed to initialize. (%d)", vpx_err); 142 return UNKNOWN_ERROR; 143 } 144 145 return OK; 146} 147 148OMX_ERRORTYPE SoftVPX::internalGetParameter( 149 OMX_INDEXTYPE index, OMX_PTR params) { 150 switch (index) { 151 case OMX_IndexParamVideoPortFormat: 152 { 153 OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = 154 (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; 155 156 if (formatParams->nPortIndex > 1) { 157 return OMX_ErrorUndefined; 158 } 159 160 if (formatParams->nIndex != 0) { 161 return OMX_ErrorNoMore; 162 } 163 164 if (formatParams->nPortIndex == 0) { 165 formatParams->eCompressionFormat = OMX_VIDEO_CodingVPX; 166 formatParams->eColorFormat = OMX_COLOR_FormatUnused; 167 formatParams->xFramerate = 0; 168 } else { 169 CHECK_EQ(formatParams->nPortIndex, 1u); 170 171 formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; 172 formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; 173 formatParams->xFramerate = 0; 174 } 175 176 return OMX_ErrorNone; 177 } 178 179 default: 180 return SimpleSoftOMXComponent::internalGetParameter(index, params); 181 } 182} 183 184OMX_ERRORTYPE SoftVPX::internalSetParameter( 185 OMX_INDEXTYPE index, const OMX_PTR params) { 186 switch (index) { 187 case OMX_IndexParamStandardComponentRole: 188 { 189 const OMX_PARAM_COMPONENTROLETYPE *roleParams = 190 (const OMX_PARAM_COMPONENTROLETYPE *)params; 191 192 if (strncmp((const char *)roleParams->cRole, 193 "video_decoder.vpx", 194 OMX_MAX_STRINGNAME_SIZE - 1)) { 195 return OMX_ErrorUndefined; 196 } 197 198 return OMX_ErrorNone; 199 } 200 201 case OMX_IndexParamVideoPortFormat: 202 { 203 OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = 204 (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; 205 206 if (formatParams->nPortIndex > 1) { 207 return OMX_ErrorUndefined; 208 } 209 210 if (formatParams->nIndex != 0) { 211 return OMX_ErrorNoMore; 212 } 213 214 return OMX_ErrorNone; 215 } 216 217 default: 218 return SimpleSoftOMXComponent::internalSetParameter(index, params); 219 } 220} 221 222void SoftVPX::onQueueFilled(OMX_U32 portIndex) { 223 if (mOutputPortSettingsChange != NONE) { 224 return; 225 } 226 227 List<BufferInfo *> &inQueue = getPortQueue(0); 228 List<BufferInfo *> &outQueue = getPortQueue(1); 229 bool EOSseen = false; 230 231 while (!inQueue.empty() && !outQueue.empty()) { 232 BufferInfo *inInfo = *inQueue.begin(); 233 OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; 234 235 BufferInfo *outInfo = *outQueue.begin(); 236 OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; 237 238 if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { 239 EOSseen = true; 240 if (inHeader->nFilledLen == 0) { 241 inQueue.erase(inQueue.begin()); 242 inInfo->mOwnedByUs = false; 243 notifyEmptyBufferDone(inHeader); 244 245 outHeader->nFilledLen = 0; 246 outHeader->nFlags = OMX_BUFFERFLAG_EOS; 247 248 outQueue.erase(outQueue.begin()); 249 outInfo->mOwnedByUs = false; 250 notifyFillBufferDone(outHeader); 251 return; 252 } 253 } 254 255 if (vpx_codec_decode( 256 (vpx_codec_ctx_t *)mCtx, 257 inHeader->pBuffer + inHeader->nOffset, 258 inHeader->nFilledLen, 259 NULL, 260 0)) { 261 ALOGE("on2 decoder failed to decode frame."); 262 263 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 264 return; 265 } 266 267 vpx_codec_iter_t iter = NULL; 268 vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); 269 270 if (img != NULL) { 271 CHECK_EQ(img->fmt, IMG_FMT_I420); 272 273 int32_t width = img->d_w; 274 int32_t height = img->d_h; 275 276 if (width != mWidth || height != mHeight) { 277 mWidth = width; 278 mHeight = height; 279 280 updatePortDefinitions(); 281 282 notify(OMX_EventPortSettingsChanged, 1, 0, NULL); 283 mOutputPortSettingsChange = AWAITING_DISABLED; 284 return; 285 } 286 287 outHeader->nOffset = 0; 288 outHeader->nFilledLen = (width * height * 3) / 2; 289 outHeader->nFlags = EOSseen ? OMX_BUFFERFLAG_EOS : 0; 290 outHeader->nTimeStamp = inHeader->nTimeStamp; 291 292 const uint8_t *srcLine = (const uint8_t *)img->planes[PLANE_Y]; 293 uint8_t *dst = outHeader->pBuffer; 294 for (size_t i = 0; i < img->d_h; ++i) { 295 memcpy(dst, srcLine, img->d_w); 296 297 srcLine += img->stride[PLANE_Y]; 298 dst += img->d_w; 299 } 300 301 srcLine = (const uint8_t *)img->planes[PLANE_U]; 302 for (size_t i = 0; i < img->d_h / 2; ++i) { 303 memcpy(dst, srcLine, img->d_w / 2); 304 305 srcLine += img->stride[PLANE_U]; 306 dst += img->d_w / 2; 307 } 308 309 srcLine = (const uint8_t *)img->planes[PLANE_V]; 310 for (size_t i = 0; i < img->d_h / 2; ++i) { 311 memcpy(dst, srcLine, img->d_w / 2); 312 313 srcLine += img->stride[PLANE_V]; 314 dst += img->d_w / 2; 315 } 316 317 outInfo->mOwnedByUs = false; 318 outQueue.erase(outQueue.begin()); 319 outInfo = NULL; 320 notifyFillBufferDone(outHeader); 321 outHeader = NULL; 322 } 323 324 inInfo->mOwnedByUs = false; 325 inQueue.erase(inQueue.begin()); 326 inInfo = NULL; 327 notifyEmptyBufferDone(inHeader); 328 inHeader = NULL; 329 } 330} 331 332void SoftVPX::onPortFlushCompleted(OMX_U32 portIndex) { 333} 334 335void SoftVPX::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { 336 if (portIndex != 1) { 337 return; 338 } 339 340 switch (mOutputPortSettingsChange) { 341 case NONE: 342 break; 343 344 case AWAITING_DISABLED: 345 { 346 CHECK(!enabled); 347 mOutputPortSettingsChange = AWAITING_ENABLED; 348 break; 349 } 350 351 default: 352 { 353 CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); 354 CHECK(enabled); 355 mOutputPortSettingsChange = NONE; 356 break; 357 } 358 } 359} 360 361void SoftVPX::onReset() { 362 mOutputPortSettingsChange = NONE; 363} 364 365void SoftVPX::updatePortDefinitions() { 366 OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef; 367 def->format.video.nFrameWidth = mWidth; 368 def->format.video.nFrameHeight = mHeight; 369 def->format.video.nStride = def->format.video.nFrameWidth; 370 def->format.video.nSliceHeight = def->format.video.nFrameHeight; 371 372 def = &editPortInfo(1)->mDef; 373 def->format.video.nFrameWidth = mWidth; 374 def->format.video.nFrameHeight = mHeight; 375 def->format.video.nStride = def->format.video.nFrameWidth; 376 def->format.video.nSliceHeight = def->format.video.nFrameHeight; 377 378 def->nBufferSize = 379 (def->format.video.nFrameWidth 380 * def->format.video.nFrameHeight * 3) / 2; 381} 382 383} // namespace android 384 385android::SoftOMXComponent *createSoftOMXComponent( 386 const char *name, const OMX_CALLBACKTYPE *callbacks, 387 OMX_PTR appData, OMX_COMPONENTTYPE **component) { 388 return new android::SoftVPX(name, callbacks, appData, component); 389} 390 391