SoftVideoDecoderOMXComponent.cpp revision a0940a569f2bc24b00dc10ce0fa7658b1dc3a3a5
1/* 2 * Copyright (C) 2013 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#include <inttypes.h> 18 19//#define LOG_NDEBUG 0 20#define LOG_TAG "SoftVideoDecoderOMXComponent" 21#include <utils/Log.h> 22 23#include "include/SoftVideoDecoderOMXComponent.h" 24 25#include <media/hardware/HardwareAPI.h> 26#include <media/stagefright/foundation/ADebug.h> 27#include <media/stagefright/foundation/ALooper.h> 28#include <media/stagefright/foundation/AMessage.h> 29#include <media/stagefright/foundation/AUtils.h> 30#include <media/stagefright/MediaDefs.h> 31 32namespace android { 33 34template<class T> 35static void InitOMXParams(T *params) { 36 params->nSize = sizeof(T); 37 params->nVersion.s.nVersionMajor = 1; 38 params->nVersion.s.nVersionMinor = 0; 39 params->nVersion.s.nRevision = 0; 40 params->nVersion.s.nStep = 0; 41} 42 43SoftVideoDecoderOMXComponent::SoftVideoDecoderOMXComponent( 44 const char *name, 45 const char *componentRole, 46 OMX_VIDEO_CODINGTYPE codingType, 47 const CodecProfileLevel *profileLevels, 48 size_t numProfileLevels, 49 int32_t width, 50 int32_t height, 51 const OMX_CALLBACKTYPE *callbacks, 52 OMX_PTR appData, 53 OMX_COMPONENTTYPE **component) 54 : SimpleSoftOMXComponent(name, callbacks, appData, component), 55 mIsAdaptive(false), 56 mAdaptiveMaxWidth(0), 57 mAdaptiveMaxHeight(0), 58 mWidth(width), 59 mHeight(height), 60 mCropLeft(0), 61 mCropTop(0), 62 mCropWidth(width), 63 mCropHeight(height), 64 mOutputPortSettingsChange(NONE), 65 mMinInputBufferSize(384), // arbitrary, using one uncompressed macroblock 66 mMinCompressionRatio(1), // max input size is normally the output size 67 mComponentRole(componentRole), 68 mCodingType(codingType), 69 mProfileLevels(profileLevels), 70 mNumProfileLevels(numProfileLevels) { 71} 72 73void SoftVideoDecoderOMXComponent::initPorts( 74 OMX_U32 numInputBuffers, 75 OMX_U32 inputBufferSize, 76 OMX_U32 numOutputBuffers, 77 const char *mimeType, 78 OMX_U32 minCompressionRatio) { 79 mMinInputBufferSize = inputBufferSize; 80 mMinCompressionRatio = minCompressionRatio; 81 82 OMX_PARAM_PORTDEFINITIONTYPE def; 83 InitOMXParams(&def); 84 85 def.nPortIndex = kInputPortIndex; 86 def.eDir = OMX_DirInput; 87 def.nBufferCountMin = numInputBuffers; 88 def.nBufferCountActual = def.nBufferCountMin; 89 def.nBufferSize = inputBufferSize; 90 def.bEnabled = OMX_TRUE; 91 def.bPopulated = OMX_FALSE; 92 def.eDomain = OMX_PortDomainVideo; 93 def.bBuffersContiguous = OMX_FALSE; 94 def.nBufferAlignment = 1; 95 96 def.format.video.cMIMEType = const_cast<char *>(mimeType); 97 def.format.video.pNativeRender = NULL; 98 /* size is initialized in updatePortDefinitions() */ 99 def.format.video.nBitrate = 0; 100 def.format.video.xFramerate = 0; 101 def.format.video.bFlagErrorConcealment = OMX_FALSE; 102 def.format.video.eCompressionFormat = mCodingType; 103 def.format.video.eColorFormat = OMX_COLOR_FormatUnused; 104 def.format.video.pNativeWindow = NULL; 105 106 addPort(def); 107 108 def.nPortIndex = kOutputPortIndex; 109 def.eDir = OMX_DirOutput; 110 def.nBufferCountMin = numOutputBuffers; 111 def.nBufferCountActual = def.nBufferCountMin; 112 def.bEnabled = OMX_TRUE; 113 def.bPopulated = OMX_FALSE; 114 def.eDomain = OMX_PortDomainVideo; 115 def.bBuffersContiguous = OMX_FALSE; 116 def.nBufferAlignment = 2; 117 118 def.format.video.cMIMEType = const_cast<char *>("video/raw"); 119 def.format.video.pNativeRender = NULL; 120 /* size is initialized in updatePortDefinitions() */ 121 def.format.video.nBitrate = 0; 122 def.format.video.xFramerate = 0; 123 def.format.video.bFlagErrorConcealment = OMX_FALSE; 124 def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; 125 def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; 126 def.format.video.pNativeWindow = NULL; 127 128 addPort(def); 129 130 updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */); 131} 132 133void SoftVideoDecoderOMXComponent::updatePortDefinitions(bool updateCrop, bool updateInputSize) { 134 OMX_PARAM_PORTDEFINITIONTYPE *outDef = &editPortInfo(kOutputPortIndex)->mDef; 135 outDef->format.video.nFrameWidth = outputBufferWidth(); 136 outDef->format.video.nFrameHeight = outputBufferHeight(); 137 outDef->format.video.nStride = outDef->format.video.nFrameWidth; 138 outDef->format.video.nSliceHeight = outDef->format.video.nFrameHeight; 139 140 outDef->nBufferSize = 141 (outDef->format.video.nStride * outDef->format.video.nSliceHeight * 3) / 2; 142 143 OMX_PARAM_PORTDEFINITIONTYPE *inDef = &editPortInfo(kInputPortIndex)->mDef; 144 inDef->format.video.nFrameWidth = mWidth; 145 inDef->format.video.nFrameHeight = mHeight; 146 // input port is compressed, hence it has no stride 147 inDef->format.video.nStride = 0; 148 inDef->format.video.nSliceHeight = 0; 149 150 // when output format changes, input buffer size does not actually change 151 if (updateInputSize) { 152 inDef->nBufferSize = max(outDef->nBufferSize / mMinCompressionRatio, mMinInputBufferSize); 153 } 154 155 if (updateCrop) { 156 mCropLeft = 0; 157 mCropTop = 0; 158 mCropWidth = mWidth; 159 mCropHeight = mHeight; 160 } 161} 162 163 164uint32_t SoftVideoDecoderOMXComponent::outputBufferWidth() { 165 return mIsAdaptive ? mAdaptiveMaxWidth : mWidth; 166} 167 168uint32_t SoftVideoDecoderOMXComponent::outputBufferHeight() { 169 return mIsAdaptive ? mAdaptiveMaxHeight : mHeight; 170} 171 172void SoftVideoDecoderOMXComponent::handlePortSettingsChange( 173 bool *portWillReset, uint32_t width, uint32_t height, 174 CropSettingsMode cropSettingsMode, bool fakeStride) { 175 *portWillReset = false; 176 bool sizeChanged = (width != mWidth || height != mHeight); 177 bool updateCrop = (cropSettingsMode == kCropUnSet); 178 bool cropChanged = (cropSettingsMode == kCropChanged); 179 bool strideChanged = false; 180 if (fakeStride) { 181 OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef; 182 if (def->format.video.nStride != (OMX_S32)width 183 || def->format.video.nSliceHeight != (OMX_U32)height) { 184 strideChanged = true; 185 } 186 } 187 188 if (sizeChanged || cropChanged || strideChanged) { 189 mWidth = width; 190 mHeight = height; 191 192 if ((sizeChanged && !mIsAdaptive) 193 || width > mAdaptiveMaxWidth 194 || height > mAdaptiveMaxHeight) { 195 if (mIsAdaptive) { 196 if (width > mAdaptiveMaxWidth) { 197 mAdaptiveMaxWidth = width; 198 } 199 if (height > mAdaptiveMaxHeight) { 200 mAdaptiveMaxHeight = height; 201 } 202 } 203 updatePortDefinitions(updateCrop); 204 notify(OMX_EventPortSettingsChanged, kOutputPortIndex, 0, NULL); 205 mOutputPortSettingsChange = AWAITING_DISABLED; 206 *portWillReset = true; 207 } else { 208 updatePortDefinitions(updateCrop); 209 210 if (fakeStride) { 211 // MAJOR HACK that is not pretty, it's just to fool the renderer to read the correct 212 // data. 213 // Some software decoders (e.g. SoftMPEG4) fill decoded frame directly to output 214 // buffer without considering the output buffer stride and slice height. So this is 215 // used to signal how the buffer is arranged. The alternative is to re-arrange the 216 // output buffer in SoftMPEG4, but that results in memcopies. 217 OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef; 218 def->format.video.nStride = mWidth; 219 def->format.video.nSliceHeight = mHeight; 220 } 221 222 notify(OMX_EventPortSettingsChanged, kOutputPortIndex, 223 OMX_IndexConfigCommonOutputCrop, NULL); 224 } 225 } 226} 227 228void SoftVideoDecoderOMXComponent::copyYV12FrameToOutputBuffer( 229 uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV, 230 size_t srcYStride, size_t srcUStride, size_t srcVStride) { 231 size_t dstYStride = outputBufferWidth(); 232 size_t dstUVStride = dstYStride / 2; 233 size_t dstHeight = outputBufferHeight(); 234 uint8_t *dstStart = dst; 235 236 for (size_t i = 0; i < mHeight; ++i) { 237 memcpy(dst, srcY, mWidth); 238 srcY += srcYStride; 239 dst += dstYStride; 240 } 241 242 dst = dstStart + dstYStride * dstHeight; 243 for (size_t i = 0; i < mHeight / 2; ++i) { 244 memcpy(dst, srcU, mWidth / 2); 245 srcU += srcUStride; 246 dst += dstUVStride; 247 } 248 249 dst = dstStart + (5 * dstYStride * dstHeight) / 4; 250 for (size_t i = 0; i < mHeight / 2; ++i) { 251 memcpy(dst, srcV, mWidth / 2); 252 srcV += srcVStride; 253 dst += dstUVStride; 254 } 255} 256 257OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalGetParameter( 258 OMX_INDEXTYPE index, OMX_PTR params) { 259 switch (index) { 260 case OMX_IndexParamVideoPortFormat: 261 { 262 OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = 263 (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; 264 265 if (formatParams->nPortIndex > kMaxPortIndex) { 266 return OMX_ErrorBadPortIndex; 267 } 268 269 if (formatParams->nIndex != 0) { 270 return OMX_ErrorNoMore; 271 } 272 273 if (formatParams->nPortIndex == kInputPortIndex) { 274 formatParams->eCompressionFormat = mCodingType; 275 formatParams->eColorFormat = OMX_COLOR_FormatUnused; 276 formatParams->xFramerate = 0; 277 } else { 278 CHECK_EQ(formatParams->nPortIndex, 1u); 279 280 formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; 281 formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; 282 formatParams->xFramerate = 0; 283 } 284 285 return OMX_ErrorNone; 286 } 287 288 case OMX_IndexParamVideoProfileLevelQuerySupported: 289 { 290 OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = 291 (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) params; 292 293 if (profileLevel->nPortIndex != kInputPortIndex) { 294 ALOGE("Invalid port index: %" PRIu32, profileLevel->nPortIndex); 295 return OMX_ErrorUnsupportedIndex; 296 } 297 298 if (profileLevel->nProfileIndex >= mNumProfileLevels) { 299 return OMX_ErrorNoMore; 300 } 301 302 profileLevel->eProfile = mProfileLevels[profileLevel->nProfileIndex].mProfile; 303 profileLevel->eLevel = mProfileLevels[profileLevel->nProfileIndex].mLevel; 304 return OMX_ErrorNone; 305 } 306 307 default: 308 return SimpleSoftOMXComponent::internalGetParameter(index, params); 309 } 310} 311 312OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalSetParameter( 313 OMX_INDEXTYPE index, const OMX_PTR params) { 314 // Include extension index OMX_INDEXEXTTYPE. 315 const int32_t indexFull = index; 316 317 switch (indexFull) { 318 case OMX_IndexParamStandardComponentRole: 319 { 320 const OMX_PARAM_COMPONENTROLETYPE *roleParams = 321 (const OMX_PARAM_COMPONENTROLETYPE *)params; 322 323 if (strncmp((const char *)roleParams->cRole, 324 mComponentRole, 325 OMX_MAX_STRINGNAME_SIZE - 1)) { 326 return OMX_ErrorUndefined; 327 } 328 329 return OMX_ErrorNone; 330 } 331 332 case OMX_IndexParamVideoPortFormat: 333 { 334 OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = 335 (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; 336 337 if (formatParams->nPortIndex > kMaxPortIndex) { 338 return OMX_ErrorBadPortIndex; 339 } 340 341 if (formatParams->nIndex != 0) { 342 return OMX_ErrorNoMore; 343 } 344 345 if (formatParams->nPortIndex == kInputPortIndex) { 346 if (formatParams->eCompressionFormat != mCodingType 347 || formatParams->eColorFormat != OMX_COLOR_FormatUnused) { 348 return OMX_ErrorUnsupportedSetting; 349 } 350 } else { 351 if (formatParams->eCompressionFormat != OMX_VIDEO_CodingUnused 352 || formatParams->eColorFormat != OMX_COLOR_FormatYUV420Planar) { 353 return OMX_ErrorUnsupportedSetting; 354 } 355 } 356 357 return OMX_ErrorNone; 358 } 359 360 case kPrepareForAdaptivePlaybackIndex: 361 { 362 const PrepareForAdaptivePlaybackParams* adaptivePlaybackParams = 363 (const PrepareForAdaptivePlaybackParams *)params; 364 mIsAdaptive = adaptivePlaybackParams->bEnable; 365 if (mIsAdaptive) { 366 mAdaptiveMaxWidth = adaptivePlaybackParams->nMaxFrameWidth; 367 mAdaptiveMaxHeight = adaptivePlaybackParams->nMaxFrameHeight; 368 mWidth = mAdaptiveMaxWidth; 369 mHeight = mAdaptiveMaxHeight; 370 } else { 371 mAdaptiveMaxWidth = 0; 372 mAdaptiveMaxHeight = 0; 373 } 374 updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */); 375 return OMX_ErrorNone; 376 } 377 378 case OMX_IndexParamPortDefinition: 379 { 380 OMX_PARAM_PORTDEFINITIONTYPE *newParams = 381 (OMX_PARAM_PORTDEFINITIONTYPE *)params; 382 OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &newParams->format.video; 383 OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(newParams->nPortIndex)->mDef; 384 385 uint32_t oldWidth = def->format.video.nFrameWidth; 386 uint32_t oldHeight = def->format.video.nFrameHeight; 387 uint32_t newWidth = video_def->nFrameWidth; 388 uint32_t newHeight = video_def->nFrameHeight; 389 if (newWidth != oldWidth || newHeight != oldHeight) { 390 bool outputPort = (newParams->nPortIndex == kOutputPortIndex); 391 def->format.video.nFrameWidth = 392 (mIsAdaptive && outputPort) ? mAdaptiveMaxWidth : newWidth; 393 def->format.video.nFrameHeight = 394 (mIsAdaptive && outputPort) ? mAdaptiveMaxHeight : newHeight; 395 if (outputPort) { 396 def->format.video.nStride = def->format.video.nFrameWidth; 397 def->format.video.nSliceHeight = def->format.video.nFrameHeight; 398 def->nBufferSize = 399 def->format.video.nStride * def->format.video.nSliceHeight * 3 / 2; 400 401 402 OMX_PARAM_PORTDEFINITIONTYPE *inDef = &editPortInfo(kInputPortIndex)->mDef; 403 // increase input buffer size if required 404 inDef->nBufferSize = 405 max(def->nBufferSize / mMinCompressionRatio, inDef->nBufferSize); 406 407 mWidth = newWidth; 408 mHeight = newHeight; 409 mCropLeft = 0; 410 mCropTop = 0; 411 mCropWidth = newWidth; 412 mCropHeight = newHeight; 413 } 414 newParams->nBufferSize = def->nBufferSize; 415 } 416 return SimpleSoftOMXComponent::internalSetParameter(index, params); 417 } 418 419 default: 420 return SimpleSoftOMXComponent::internalSetParameter(index, params); 421 } 422} 423 424OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getConfig( 425 OMX_INDEXTYPE index, OMX_PTR params) { 426 switch (index) { 427 case OMX_IndexConfigCommonOutputCrop: 428 { 429 OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params; 430 431 if (rectParams->nPortIndex != kOutputPortIndex) { 432 return OMX_ErrorUndefined; 433 } 434 435 rectParams->nLeft = mCropLeft; 436 rectParams->nTop = mCropTop; 437 rectParams->nWidth = mCropWidth; 438 rectParams->nHeight = mCropHeight; 439 440 return OMX_ErrorNone; 441 } 442 443 default: 444 return OMX_ErrorUnsupportedIndex; 445 } 446} 447 448OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getExtensionIndex( 449 const char *name, OMX_INDEXTYPE *index) { 450 if (!strcmp(name, "OMX.google.android.index.prepareForAdaptivePlayback")) { 451 *(int32_t*)index = kPrepareForAdaptivePlaybackIndex; 452 return OMX_ErrorNone; 453 } 454 455 return SimpleSoftOMXComponent::getExtensionIndex(name, index); 456} 457 458void SoftVideoDecoderOMXComponent::onReset() { 459 mOutputPortSettingsChange = NONE; 460} 461 462void SoftVideoDecoderOMXComponent::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { 463 if (portIndex != kOutputPortIndex) { 464 return; 465 } 466 467 switch (mOutputPortSettingsChange) { 468 case NONE: 469 break; 470 471 case AWAITING_DISABLED: 472 { 473 CHECK(!enabled); 474 mOutputPortSettingsChange = AWAITING_ENABLED; 475 break; 476 } 477 478 default: 479 { 480 CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); 481 CHECK(enabled); 482 mOutputPortSettingsChange = NONE; 483 break; 484 } 485 } 486} 487 488} // namespace android 489