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( 153 outDef->nBufferSize / mMinCompressionRatio, 154 max(mMinInputBufferSize, inDef->nBufferSize)); 155 } 156 157 if (updateCrop) { 158 mCropLeft = 0; 159 mCropTop = 0; 160 mCropWidth = mWidth; 161 mCropHeight = mHeight; 162 } 163} 164 165 166uint32_t SoftVideoDecoderOMXComponent::outputBufferWidth() { 167 return mIsAdaptive ? mAdaptiveMaxWidth : mWidth; 168} 169 170uint32_t SoftVideoDecoderOMXComponent::outputBufferHeight() { 171 return mIsAdaptive ? mAdaptiveMaxHeight : mHeight; 172} 173 174void SoftVideoDecoderOMXComponent::handlePortSettingsChange( 175 bool *portWillReset, uint32_t width, uint32_t height, 176 CropSettingsMode cropSettingsMode, bool fakeStride) { 177 *portWillReset = false; 178 bool sizeChanged = (width != mWidth || height != mHeight); 179 bool updateCrop = (cropSettingsMode == kCropUnSet); 180 bool cropChanged = (cropSettingsMode == kCropChanged); 181 bool strideChanged = false; 182 if (fakeStride) { 183 OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef; 184 if (def->format.video.nStride != (OMX_S32)width 185 || def->format.video.nSliceHeight != (OMX_U32)height) { 186 strideChanged = true; 187 } 188 } 189 190 if (sizeChanged || cropChanged || strideChanged) { 191 mWidth = width; 192 mHeight = height; 193 194 if ((sizeChanged && !mIsAdaptive) 195 || width > mAdaptiveMaxWidth 196 || height > mAdaptiveMaxHeight) { 197 if (mIsAdaptive) { 198 if (width > mAdaptiveMaxWidth) { 199 mAdaptiveMaxWidth = width; 200 } 201 if (height > mAdaptiveMaxHeight) { 202 mAdaptiveMaxHeight = height; 203 } 204 } 205 updatePortDefinitions(updateCrop); 206 notify(OMX_EventPortSettingsChanged, kOutputPortIndex, 0, NULL); 207 mOutputPortSettingsChange = AWAITING_DISABLED; 208 *portWillReset = true; 209 } else { 210 updatePortDefinitions(updateCrop); 211 212 if (fakeStride) { 213 // MAJOR HACK that is not pretty, it's just to fool the renderer to read the correct 214 // data. 215 // Some software decoders (e.g. SoftMPEG4) fill decoded frame directly to output 216 // buffer without considering the output buffer stride and slice height. So this is 217 // used to signal how the buffer is arranged. The alternative is to re-arrange the 218 // output buffer in SoftMPEG4, but that results in memcopies. 219 OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef; 220 def->format.video.nStride = mWidth; 221 def->format.video.nSliceHeight = mHeight; 222 } 223 224 notify(OMX_EventPortSettingsChanged, kOutputPortIndex, 225 OMX_IndexConfigCommonOutputCrop, NULL); 226 } 227 } 228} 229 230void SoftVideoDecoderOMXComponent::copyYV12FrameToOutputBuffer( 231 uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV, 232 size_t srcYStride, size_t srcUStride, size_t srcVStride) { 233 size_t dstYStride = outputBufferWidth(); 234 size_t dstUVStride = dstYStride / 2; 235 size_t dstHeight = outputBufferHeight(); 236 uint8_t *dstStart = dst; 237 238 for (size_t i = 0; i < mHeight; ++i) { 239 memcpy(dst, srcY, mWidth); 240 srcY += srcYStride; 241 dst += dstYStride; 242 } 243 244 dst = dstStart + dstYStride * dstHeight; 245 for (size_t i = 0; i < mHeight / 2; ++i) { 246 memcpy(dst, srcU, mWidth / 2); 247 srcU += srcUStride; 248 dst += dstUVStride; 249 } 250 251 dst = dstStart + (5 * dstYStride * dstHeight) / 4; 252 for (size_t i = 0; i < mHeight / 2; ++i) { 253 memcpy(dst, srcV, mWidth / 2); 254 srcV += srcVStride; 255 dst += dstUVStride; 256 } 257} 258 259OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalGetParameter( 260 OMX_INDEXTYPE index, OMX_PTR params) { 261 switch (index) { 262 case OMX_IndexParamVideoPortFormat: 263 { 264 OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = 265 (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; 266 267 if (formatParams->nPortIndex > kMaxPortIndex) { 268 return OMX_ErrorBadPortIndex; 269 } 270 271 if (formatParams->nIndex != 0) { 272 return OMX_ErrorNoMore; 273 } 274 275 if (formatParams->nPortIndex == kInputPortIndex) { 276 formatParams->eCompressionFormat = mCodingType; 277 formatParams->eColorFormat = OMX_COLOR_FormatUnused; 278 formatParams->xFramerate = 0; 279 } else { 280 CHECK_EQ(formatParams->nPortIndex, 1u); 281 282 formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; 283 formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; 284 formatParams->xFramerate = 0; 285 } 286 287 return OMX_ErrorNone; 288 } 289 290 case OMX_IndexParamVideoProfileLevelQuerySupported: 291 { 292 OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = 293 (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) params; 294 295 if (profileLevel->nPortIndex != kInputPortIndex) { 296 ALOGE("Invalid port index: %" PRIu32, profileLevel->nPortIndex); 297 return OMX_ErrorUnsupportedIndex; 298 } 299 300 if (profileLevel->nProfileIndex >= mNumProfileLevels) { 301 return OMX_ErrorNoMore; 302 } 303 304 profileLevel->eProfile = mProfileLevels[profileLevel->nProfileIndex].mProfile; 305 profileLevel->eLevel = mProfileLevels[profileLevel->nProfileIndex].mLevel; 306 return OMX_ErrorNone; 307 } 308 309 default: 310 return SimpleSoftOMXComponent::internalGetParameter(index, params); 311 } 312} 313 314OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalSetParameter( 315 OMX_INDEXTYPE index, const OMX_PTR params) { 316 // Include extension index OMX_INDEXEXTTYPE. 317 const int32_t indexFull = index; 318 319 switch (indexFull) { 320 case OMX_IndexParamStandardComponentRole: 321 { 322 const OMX_PARAM_COMPONENTROLETYPE *roleParams = 323 (const OMX_PARAM_COMPONENTROLETYPE *)params; 324 325 if (strncmp((const char *)roleParams->cRole, 326 mComponentRole, 327 OMX_MAX_STRINGNAME_SIZE - 1)) { 328 return OMX_ErrorUndefined; 329 } 330 331 return OMX_ErrorNone; 332 } 333 334 case OMX_IndexParamVideoPortFormat: 335 { 336 OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = 337 (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; 338 339 if (formatParams->nPortIndex > kMaxPortIndex) { 340 return OMX_ErrorBadPortIndex; 341 } 342 343 if (formatParams->nIndex != 0) { 344 return OMX_ErrorNoMore; 345 } 346 347 if (formatParams->nPortIndex == kInputPortIndex) { 348 if (formatParams->eCompressionFormat != mCodingType 349 || formatParams->eColorFormat != OMX_COLOR_FormatUnused) { 350 return OMX_ErrorUnsupportedSetting; 351 } 352 } else { 353 if (formatParams->eCompressionFormat != OMX_VIDEO_CodingUnused 354 || formatParams->eColorFormat != OMX_COLOR_FormatYUV420Planar) { 355 return OMX_ErrorUnsupportedSetting; 356 } 357 } 358 359 return OMX_ErrorNone; 360 } 361 362 case kPrepareForAdaptivePlaybackIndex: 363 { 364 const PrepareForAdaptivePlaybackParams* adaptivePlaybackParams = 365 (const PrepareForAdaptivePlaybackParams *)params; 366 mIsAdaptive = adaptivePlaybackParams->bEnable; 367 if (mIsAdaptive) { 368 mAdaptiveMaxWidth = adaptivePlaybackParams->nMaxFrameWidth; 369 mAdaptiveMaxHeight = adaptivePlaybackParams->nMaxFrameHeight; 370 mWidth = mAdaptiveMaxWidth; 371 mHeight = mAdaptiveMaxHeight; 372 } else { 373 mAdaptiveMaxWidth = 0; 374 mAdaptiveMaxHeight = 0; 375 } 376 updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */); 377 return OMX_ErrorNone; 378 } 379 380 case OMX_IndexParamPortDefinition: 381 { 382 OMX_PARAM_PORTDEFINITIONTYPE *newParams = 383 (OMX_PARAM_PORTDEFINITIONTYPE *)params; 384 OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &newParams->format.video; 385 OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(newParams->nPortIndex)->mDef; 386 387 uint32_t oldWidth = def->format.video.nFrameWidth; 388 uint32_t oldHeight = def->format.video.nFrameHeight; 389 uint32_t newWidth = video_def->nFrameWidth; 390 uint32_t newHeight = video_def->nFrameHeight; 391 if (newWidth != oldWidth || newHeight != oldHeight) { 392 bool outputPort = (newParams->nPortIndex == kOutputPortIndex); 393 if (outputPort) { 394 // only update (essentially crop) if size changes 395 mWidth = newWidth; 396 mHeight = newHeight; 397 398 updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */); 399 // reset buffer size based on frame size 400 newParams->nBufferSize = def->nBufferSize; 401 } else { 402 // For input port, we only set nFrameWidth and nFrameHeight. Buffer size 403 // is updated when configuring the output port using the max-frame-size, 404 // though client can still request a larger size. 405 def->format.video.nFrameWidth = newWidth; 406 def->format.video.nFrameHeight = newHeight; 407 } 408 } 409 return SimpleSoftOMXComponent::internalSetParameter(index, params); 410 } 411 412 default: 413 return SimpleSoftOMXComponent::internalSetParameter(index, params); 414 } 415} 416 417OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getConfig( 418 OMX_INDEXTYPE index, OMX_PTR params) { 419 switch (index) { 420 case OMX_IndexConfigCommonOutputCrop: 421 { 422 OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params; 423 424 if (rectParams->nPortIndex != kOutputPortIndex) { 425 return OMX_ErrorUndefined; 426 } 427 428 rectParams->nLeft = mCropLeft; 429 rectParams->nTop = mCropTop; 430 rectParams->nWidth = mCropWidth; 431 rectParams->nHeight = mCropHeight; 432 433 return OMX_ErrorNone; 434 } 435 436 default: 437 return OMX_ErrorUnsupportedIndex; 438 } 439} 440 441OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getExtensionIndex( 442 const char *name, OMX_INDEXTYPE *index) { 443 if (!strcmp(name, "OMX.google.android.index.prepareForAdaptivePlayback")) { 444 *(int32_t*)index = kPrepareForAdaptivePlaybackIndex; 445 return OMX_ErrorNone; 446 } 447 448 return SimpleSoftOMXComponent::getExtensionIndex(name, index); 449} 450 451void SoftVideoDecoderOMXComponent::onReset() { 452 mOutputPortSettingsChange = NONE; 453} 454 455void SoftVideoDecoderOMXComponent::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { 456 if (portIndex != kOutputPortIndex) { 457 return; 458 } 459 460 switch (mOutputPortSettingsChange) { 461 case NONE: 462 break; 463 464 case AWAITING_DISABLED: 465 { 466 CHECK(!enabled); 467 mOutputPortSettingsChange = AWAITING_ENABLED; 468 break; 469 } 470 471 default: 472 { 473 CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); 474 CHECK(enabled); 475 mOutputPortSettingsChange = NONE; 476 break; 477 } 478 } 479} 480 481} // namespace android 482