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 (!isValidOMXParam(formatParams)) { 268 return OMX_ErrorBadParameter; 269 } 270 271 if (formatParams->nPortIndex > kMaxPortIndex) { 272 return OMX_ErrorBadPortIndex; 273 } 274 275 if (formatParams->nIndex != 0) { 276 return OMX_ErrorNoMore; 277 } 278 279 if (formatParams->nPortIndex == kInputPortIndex) { 280 formatParams->eCompressionFormat = mCodingType; 281 formatParams->eColorFormat = OMX_COLOR_FormatUnused; 282 formatParams->xFramerate = 0; 283 } else { 284 CHECK_EQ(formatParams->nPortIndex, 1u); 285 286 formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; 287 formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; 288 formatParams->xFramerate = 0; 289 } 290 291 return OMX_ErrorNone; 292 } 293 294 case OMX_IndexParamVideoProfileLevelQuerySupported: 295 { 296 OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = 297 (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) params; 298 299 if (!isValidOMXParam(profileLevel)) { 300 return OMX_ErrorBadParameter; 301 } 302 303 if (profileLevel->nPortIndex != kInputPortIndex) { 304 ALOGE("Invalid port index: %" PRIu32, profileLevel->nPortIndex); 305 return OMX_ErrorUnsupportedIndex; 306 } 307 308 if (profileLevel->nProfileIndex >= mNumProfileLevels) { 309 return OMX_ErrorNoMore; 310 } 311 312 profileLevel->eProfile = mProfileLevels[profileLevel->nProfileIndex].mProfile; 313 profileLevel->eLevel = mProfileLevels[profileLevel->nProfileIndex].mLevel; 314 return OMX_ErrorNone; 315 } 316 317 default: 318 return SimpleSoftOMXComponent::internalGetParameter(index, params); 319 } 320} 321 322OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalSetParameter( 323 OMX_INDEXTYPE index, const OMX_PTR params) { 324 // Include extension index OMX_INDEXEXTTYPE. 325 const int32_t indexFull = index; 326 327 switch (indexFull) { 328 case OMX_IndexParamStandardComponentRole: 329 { 330 const OMX_PARAM_COMPONENTROLETYPE *roleParams = 331 (const OMX_PARAM_COMPONENTROLETYPE *)params; 332 333 if (!isValidOMXParam(roleParams)) { 334 return OMX_ErrorBadParameter; 335 } 336 337 if (strncmp((const char *)roleParams->cRole, 338 mComponentRole, 339 OMX_MAX_STRINGNAME_SIZE - 1)) { 340 return OMX_ErrorUndefined; 341 } 342 343 return OMX_ErrorNone; 344 } 345 346 case OMX_IndexParamVideoPortFormat: 347 { 348 OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = 349 (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; 350 351 if (!isValidOMXParam(formatParams)) { 352 return OMX_ErrorBadParameter; 353 } 354 355 if (formatParams->nPortIndex > kMaxPortIndex) { 356 return OMX_ErrorBadPortIndex; 357 } 358 359 if (formatParams->nIndex != 0) { 360 return OMX_ErrorNoMore; 361 } 362 363 if (formatParams->nPortIndex == kInputPortIndex) { 364 if (formatParams->eCompressionFormat != mCodingType 365 || formatParams->eColorFormat != OMX_COLOR_FormatUnused) { 366 return OMX_ErrorUnsupportedSetting; 367 } 368 } else { 369 if (formatParams->eCompressionFormat != OMX_VIDEO_CodingUnused 370 || formatParams->eColorFormat != OMX_COLOR_FormatYUV420Planar) { 371 return OMX_ErrorUnsupportedSetting; 372 } 373 } 374 375 return OMX_ErrorNone; 376 } 377 378 case kPrepareForAdaptivePlaybackIndex: 379 { 380 const PrepareForAdaptivePlaybackParams* adaptivePlaybackParams = 381 (const PrepareForAdaptivePlaybackParams *)params; 382 383 if (!isValidOMXParam(adaptivePlaybackParams)) { 384 return OMX_ErrorBadParameter; 385 } 386 387 mIsAdaptive = adaptivePlaybackParams->bEnable; 388 if (mIsAdaptive) { 389 mAdaptiveMaxWidth = adaptivePlaybackParams->nMaxFrameWidth; 390 mAdaptiveMaxHeight = adaptivePlaybackParams->nMaxFrameHeight; 391 mWidth = mAdaptiveMaxWidth; 392 mHeight = mAdaptiveMaxHeight; 393 } else { 394 mAdaptiveMaxWidth = 0; 395 mAdaptiveMaxHeight = 0; 396 } 397 updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */); 398 return OMX_ErrorNone; 399 } 400 401 case OMX_IndexParamPortDefinition: 402 { 403 OMX_PARAM_PORTDEFINITIONTYPE *newParams = 404 (OMX_PARAM_PORTDEFINITIONTYPE *)params; 405 406 if (!isValidOMXParam(newParams)) { 407 return OMX_ErrorBadParameter; 408 } 409 410 OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &newParams->format.video; 411 OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(newParams->nPortIndex)->mDef; 412 413 uint32_t oldWidth = def->format.video.nFrameWidth; 414 uint32_t oldHeight = def->format.video.nFrameHeight; 415 uint32_t newWidth = video_def->nFrameWidth; 416 uint32_t newHeight = video_def->nFrameHeight; 417 // We need width, height, stride and slice-height to be non-zero and sensible. 418 // These values were chosen to prevent integer overflows further down the line, and do 419 // not indicate support for 32kx32k video. 420 if (newWidth > 32768 || newHeight > 32768 421 || video_def->nStride > 32768 || video_def->nSliceHeight > 32768) { 422 ALOGE("b/22885421"); 423 return OMX_ErrorBadParameter; 424 } 425 if (newWidth != oldWidth || newHeight != oldHeight) { 426 bool outputPort = (newParams->nPortIndex == kOutputPortIndex); 427 if (outputPort) { 428 // only update (essentially crop) if size changes 429 mWidth = newWidth; 430 mHeight = newHeight; 431 432 updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */); 433 // reset buffer size based on frame size 434 newParams->nBufferSize = def->nBufferSize; 435 } else { 436 // For input port, we only set nFrameWidth and nFrameHeight. Buffer size 437 // is updated when configuring the output port using the max-frame-size, 438 // though client can still request a larger size. 439 def->format.video.nFrameWidth = newWidth; 440 def->format.video.nFrameHeight = newHeight; 441 } 442 } 443 return SimpleSoftOMXComponent::internalSetParameter(index, params); 444 } 445 446 default: 447 return SimpleSoftOMXComponent::internalSetParameter(index, params); 448 } 449} 450 451OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getConfig( 452 OMX_INDEXTYPE index, OMX_PTR params) { 453 switch (index) { 454 case OMX_IndexConfigCommonOutputCrop: 455 { 456 OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params; 457 458 if (!isValidOMXParam(rectParams)) { 459 return OMX_ErrorBadParameter; 460 } 461 462 if (rectParams->nPortIndex != kOutputPortIndex) { 463 return OMX_ErrorUndefined; 464 } 465 466 rectParams->nLeft = mCropLeft; 467 rectParams->nTop = mCropTop; 468 rectParams->nWidth = mCropWidth; 469 rectParams->nHeight = mCropHeight; 470 471 return OMX_ErrorNone; 472 } 473 474 default: 475 return OMX_ErrorUnsupportedIndex; 476 } 477} 478 479OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getExtensionIndex( 480 const char *name, OMX_INDEXTYPE *index) { 481 if (!strcmp(name, "OMX.google.android.index.prepareForAdaptivePlayback")) { 482 *(int32_t*)index = kPrepareForAdaptivePlaybackIndex; 483 return OMX_ErrorNone; 484 } 485 486 return SimpleSoftOMXComponent::getExtensionIndex(name, index); 487} 488 489void SoftVideoDecoderOMXComponent::onReset() { 490 mOutputPortSettingsChange = NONE; 491} 492 493void SoftVideoDecoderOMXComponent::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { 494 if (portIndex != kOutputPortIndex) { 495 return; 496 } 497 498 switch (mOutputPortSettingsChange) { 499 case NONE: 500 break; 501 502 case AWAITING_DISABLED: 503 { 504 CHECK(!enabled); 505 mOutputPortSettingsChange = AWAITING_ENABLED; 506 break; 507 } 508 509 default: 510 { 511 CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); 512 CHECK(enabled); 513 mOutputPortSettingsChange = NONE; 514 break; 515 } 516 } 517} 518 519} // namespace android 520