text: /*
   * Copyright (C) 2013 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #include <inttypes.h>
  
  //#define LOG_NDEBUG 0
  #define LOG_TAG "SoftVideoDecoderOMXComponent"
  #include <utils/Log.h>
  
  #include <media/stagefright/omx/SoftVideoDecoderOMXComponent.h>
  
  #include <media/stagefright/foundation/ADebug.h>
  #include <media/stagefright/foundation/ALooper.h>
  #include <media/stagefright/foundation/AMessage.h>
  #include <media/stagefright/foundation/AUtils.h>
  #include <media/stagefright/foundation/MediaDefs.h>
  #include <media/hardware/HardwareAPI.h>
  
  namespace android {
  
  template<class T>
  static void InitOMXParams(T *params) {
      params->nSize = sizeof(T);
      params->nVersion.s.nVersionMajor = 1;
      params->nVersion.s.nVersionMinor = 0;
      params->nVersion.s.nRevision = 0;
      params->nVersion.s.nStep = 0;
  }
  
  SoftVideoDecoderOMXComponent::SoftVideoDecoderOMXComponent(
          const char *name,
          const char *componentRole,
          OMX_VIDEO_CODINGTYPE codingType,
          const CodecProfileLevel *profileLevels,
          size_t numProfileLevels,
          int32_t width,
          int32_t height,
          const OMX_CALLBACKTYPE *callbacks,
          OMX_PTR appData,
          OMX_COMPONENTTYPE **component)
          : SimpleSoftOMXComponent(name, callbacks, appData, component),
          mIsAdaptive(false),
          mAdaptiveMaxWidth(0),
          mAdaptiveMaxHeight(0),
          mWidth(width),
          mHeight(height),
          mCropLeft(0),
          mCropTop(0),
          mCropWidth(width),
          mCropHeight(height),
          mOutputFormat(OMX_COLOR_FormatYUV420Planar),
          mOutputPortSettingsChange(NONE),
          mUpdateColorAspects(false),
          mMinInputBufferSize(384), // arbitrary, using one uncompressed macroblock
          mMinCompressionRatio(1),  // max input size is normally the output size
          mComponentRole(componentRole),
          mCodingType(codingType),
          mProfileLevels(profileLevels),
          mNumProfileLevels(numProfileLevels) {
  
      // init all the color aspects to be Unspecified.
      memset(&mDefaultColorAspects, 0, sizeof(ColorAspects));
      memset(&mBitstreamColorAspects, 0, sizeof(ColorAspects));
      memset(&mFinalColorAspects, 0, sizeof(ColorAspects));
      memset(&mHdrStaticInfo, 0, sizeof(HDRStaticInfo));
  }
  
  void SoftVideoDecoderOMXComponent::initPorts(
          OMX_U32 numInputBuffers,
          OMX_U32 inputBufferSize,
          OMX_U32 numOutputBuffers,
          const char *mimeType,
          OMX_U32 minCompressionRatio) {
      initPorts(numInputBuffers, numInputBuffers, inputBufferSize,
              numOutputBuffers, numOutputBuffers, mimeType, minCompressionRatio);
  }
  
  void SoftVideoDecoderOMXComponent::initPorts(
          OMX_U32 numMinInputBuffers,
          OMX_U32 numInputBuffers,
          OMX_U32 inputBufferSize,
          OMX_U32 numMinOutputBuffers,
          OMX_U32 numOutputBuffers,
          const char *mimeType,
          OMX_U32 minCompressionRatio) {
      mMinInputBufferSize = inputBufferSize;
      mMinCompressionRatio = minCompressionRatio;
  
      OMX_PARAM_PORTDEFINITIONTYPE def;
      InitOMXParams(&def);
  
      def.nPortIndex = kInputPortIndex;
      def.eDir = OMX_DirInput;
      def.nBufferCountMin = numMinInputBuffers;
      def.nBufferCountActual = numInputBuffers;
      def.nBufferSize = inputBufferSize;
      def.bEnabled = OMX_TRUE;
      def.bPopulated = OMX_FALSE;
      def.eDomain = OMX_PortDomainVideo;
      def.bBuffersContiguous = OMX_FALSE;
      def.nBufferAlignment = 1;
  
      def.format.video.cMIMEType = const_cast<char *>(mimeType);
      def.format.video.pNativeRender = NULL;
      /* size is initialized in updatePortDefinitions() */
      def.format.video.nBitrate = 0;
      def.format.video.xFramerate = 0;
      def.format.video.bFlagErrorConcealment = OMX_FALSE;
      def.format.video.eCompressionFormat = mCodingType;
      def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
      def.format.video.pNativeWindow = NULL;
  
      addPort(def);
  
      def.nPortIndex = kOutputPortIndex;
      def.eDir = OMX_DirOutput;
      def.nBufferCountMin = numMinOutputBuffers;
      def.nBufferCountActual = numOutputBuffers;
      def.bEnabled = OMX_TRUE;
      def.bPopulated = OMX_FALSE;
      def.eDomain = OMX_PortDomainVideo;
      def.bBuffersContiguous = OMX_FALSE;
      def.nBufferAlignment = 2;
  
      def.format.video.cMIMEType = const_cast<char *>("video/raw");
      def.format.video.pNativeRender = NULL;
      /* size is initialized in updatePortDefinitions() */
      def.format.video.nBitrate = 0;
      def.format.video.xFramerate = 0;
      def.format.video.bFlagErrorConcealment = OMX_FALSE;
      def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
      def.format.video.pNativeWindow = NULL;
  
      addPort(def);
  
      updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */);
  }
  
  void SoftVideoDecoderOMXComponent::updatePortDefinitions(bool updateCrop, bool updateInputSize) {
      OMX_PARAM_PORTDEFINITIONTYPE *outDef = &editPortInfo(kOutputPortIndex)->mDef;
      outDef->format.video.nFrameWidth = outputBufferWidth();
      outDef->format.video.nFrameHeight = outputBufferHeight();
      outDef->format.video.eColorFormat = mOutputFormat;
      outDef->format.video.nStride = outDef->format.video.nFrameWidth;
      outDef->format.video.nSliceHeight = outDef->format.video.nFrameHeight;
  
      int32_t bpp = (mOutputFormat == OMX_COLOR_FormatYUV420Planar16) ? 2 : 1;
      outDef->nBufferSize =
          (outDef->format.video.nStride * outDef->format.video.nSliceHeight * bpp * 3) / 2;
  
      OMX_PARAM_PORTDEFINITIONTYPE *inDef = &editPortInfo(kInputPortIndex)->mDef;
      inDef->format.video.nFrameWidth = mWidth;
      inDef->format.video.nFrameHeight = mHeight;
      // input port is compressed, hence it has no stride
      inDef->format.video.nStride = 0;
      inDef->format.video.nSliceHeight = 0;
  
      // when output format changes, input buffer size does not actually change
      if (updateInputSize) {
          inDef->nBufferSize = max(
                  outDef->nBufferSize / mMinCompressionRatio,
                  max(mMinInputBufferSize, inDef->nBufferSize));
      }
  
      if (updateCrop) {
          mCropLeft = 0;
          mCropTop = 0;
          mCropWidth = mWidth;
          mCropHeight = mHeight;
      }
  }
  
  
  uint32_t SoftVideoDecoderOMXComponent::outputBufferWidth() {
      return max(mIsAdaptive ? mAdaptiveMaxWidth : 0, mWidth);
  }
  
  uint32_t SoftVideoDecoderOMXComponent::outputBufferHeight() {
      return max(mIsAdaptive ? mAdaptiveMaxHeight : 0, mHeight);
  }
  
  void SoftVideoDecoderOMXComponent::handlePortSettingsChange(
          bool *portWillReset, uint32_t width, uint32_t height,
          OMX_COLOR_FORMATTYPE outputFormat,
          CropSettingsMode cropSettingsMode, bool fakeStride) {
      *portWillReset = false;
      bool sizeChanged = (width != mWidth || height != mHeight);
      bool formatChanged = (outputFormat != mOutputFormat);
      bool updateCrop = (cropSettingsMode == kCropUnSet);
      bool cropChanged = (cropSettingsMode == kCropChanged);
      bool strideChanged = false;
      if (fakeStride) {
          OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef;
          if (def->format.video.nStride != (OMX_S32)width
                  || def->format.video.nSliceHeight != (OMX_U32)height) {
              strideChanged = true;
          }
      }
  
      if (formatChanged || sizeChanged || cropChanged || strideChanged) {
          if (formatChanged) {
              ALOGD("formatChanged: 0x%08x -> 0x%08x", mOutputFormat, outputFormat);
          }
          mOutputFormat = outputFormat;
          mWidth = width;
          mHeight = height;
  
          if ((sizeChanged && !mIsAdaptive)
              || width > mAdaptiveMaxWidth
              || height > mAdaptiveMaxHeight
              || formatChanged) {
              if (mIsAdaptive) {
                  if (width > mAdaptiveMaxWidth) {
                      mAdaptiveMaxWidth = width;
                  }
                  if (height > mAdaptiveMaxHeight) {
                      mAdaptiveMaxHeight = height;
                  }
              }
              updatePortDefinitions(updateCrop);
              notify(OMX_EventPortSettingsChanged, kOutputPortIndex, 0, NULL);
              mOutputPortSettingsChange = AWAITING_DISABLED;
              *portWillReset = true;
          } else {
              updatePortDefinitions(updateCrop);
  
              if (fakeStride) {
                  // MAJOR HACK that is not pretty, it's just to fool the renderer to read the correct
                  // data.
                  // Some software decoders (e.g. SoftMPEG4) fill decoded frame directly to output
                  // buffer without considering the output buffer stride and slice height. So this is
                  // used to signal how the buffer is arranged.  The alternative is to re-arrange the
                  // output buffer in SoftMPEG4, but that results in memcopies.
                  OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef;
                  def->format.video.nStride = mWidth;
                  def->format.video.nSliceHeight = mHeight;
              }
  
              notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
                     OMX_IndexConfigCommonOutputCrop, NULL);
          }
      } else if (mUpdateColorAspects) {
          notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
                  kDescribeColorAspectsIndex, NULL);
          mUpdateColorAspects = false;
      }
  }
  
  void SoftVideoDecoderOMXComponent::dumpColorAspects(const ColorAspects &colorAspects) {
      ALOGD("dumpColorAspects: (R:%d(%s), P:%d(%s), M:%d(%s), T:%d(%s)) ",
              colorAspects.mRange, asString(colorAspects.mRange),
              colorAspects.mPrimaries, asString(colorAspects.mPrimaries),
              colorAspects.mMatrixCoeffs, asString(colorAspects.mMatrixCoeffs),
              colorAspects.mTransfer, asString(colorAspects.mTransfer));
  }
  
  bool SoftVideoDecoderOMXComponent::colorAspectsDiffer(
          const ColorAspects &a, const ColorAspects &b) {
      if (a.mRange != b.mRange
          || a.mPrimaries != b.mPrimaries
          || a.mTransfer != b.mTransfer
          || a.mMatrixCoeffs != b.mMatrixCoeffs) {
          return true;
      }
      return false;
  }
  
  void SoftVideoDecoderOMXComponent::updateFinalColorAspects(
          const ColorAspects &otherAspects, const ColorAspects &preferredAspects) {
      Mutex::Autolock autoLock(mColorAspectsLock);
      ColorAspects newAspects;
      newAspects.mRange = preferredAspects.mRange != ColorAspects::RangeUnspecified ?
          preferredAspects.mRange : otherAspects.mRange;
      newAspects.mPrimaries = preferredAspects.mPrimaries != ColorAspects::PrimariesUnspecified ?
          preferredAspects.mPrimaries : otherAspects.mPrimaries;
      newAspects.mTransfer = preferredAspects.mTransfer != ColorAspects::TransferUnspecified ?
          preferredAspects.mTransfer : otherAspects.mTransfer;
      newAspects.mMatrixCoeffs = preferredAspects.mMatrixCoeffs != ColorAspects::MatrixUnspecified ?
          preferredAspects.mMatrixCoeffs : otherAspects.mMatrixCoeffs;
  
      // Check to see if need update mFinalColorAspects.
      if (colorAspectsDiffer(mFinalColorAspects, newAspects)) {
          mFinalColorAspects = newAspects;
          mUpdateColorAspects = true;
      }
  }
  
  status_t SoftVideoDecoderOMXComponent::handleColorAspectsChange() {
      int perference = getColorAspectPreference();
      ALOGD("Color Aspects preference: %d ", perference);
  
      if (perference == kPreferBitstream) {
          updateFinalColorAspects(mDefaultColorAspects, mBitstreamColorAspects);
      } else if (perference == kPreferContainer) {
          updateFinalColorAspects(mBitstreamColorAspects, mDefaultColorAspects);
      } else {
          return OMX_ErrorUnsupportedSetting;
      }
      return OK;
  }
  
  void SoftVideoDecoderOMXComponent::copyYV12FrameToOutputBuffer(
          uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
          size_t srcYStride, size_t srcUStride, size_t srcVStride) {
      OMX_PARAM_PORTDEFINITIONTYPE *outDef = &editPortInfo(kOutputPortIndex)->mDef;
      int32_t bpp = (outDef->format.video.eColorFormat == OMX_COLOR_FormatYUV420Planar16) ? 2 : 1;
  
      size_t dstYStride = outputBufferWidth() * bpp;
      size_t dstUVStride = dstYStride / 2;
      size_t dstHeight = outputBufferHeight();
      uint8_t *dstStart = dst;
  
      for (size_t i = 0; i < mHeight; ++i) {
           memcpy(dst, srcY, mWidth * bpp);
           srcY += srcYStride;
           dst += dstYStride;
      }
  
      dst = dstStart + dstYStride * dstHeight;
      for (size_t i = 0; i < mHeight / 2; ++i) {
           memcpy(dst, srcU, mWidth / 2 * bpp);
           srcU += srcUStride;
           dst += dstUVStride;
      }
  
      dst = dstStart + (5 * dstYStride * dstHeight) / 4;
      for (size_t i = 0; i < mHeight / 2; ++i) {
           memcpy(dst, srcV, mWidth / 2 * bpp);
           srcV += srcVStride;
           dst += dstUVStride;
      }
  }
  
  OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalGetParameter(
          OMX_INDEXTYPE index, OMX_PTR params) {
      switch (index) {
          case OMX_IndexParamVideoPortFormat:
          {
              OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
                  (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
  
              if (!isValidOMXParam(formatParams)) {
                  return OMX_ErrorBadParameter;
              }
  
              if (formatParams->nPortIndex > kMaxPortIndex) {
                  return OMX_ErrorBadPortIndex;
              }
  
              if (formatParams->nIndex != 0) {
                  return OMX_ErrorNoMore;
              }
  
              if (formatParams->nPortIndex == kInputPortIndex) {
                  formatParams->eCompressionFormat = mCodingType;
                  formatParams->eColorFormat = OMX_COLOR_FormatUnused;
                  formatParams->xFramerate = 0;
              } else {
                  CHECK_EQ(formatParams->nPortIndex, 1u);
  
                  formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
                  formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
                  formatParams->xFramerate = 0;
              }
  
              return OMX_ErrorNone;
          }
  
          case OMX_IndexParamVideoProfileLevelQuerySupported:
          {
              OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel =
                    (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) params;
  
              if (!isValidOMXParam(profileLevel)) {
                  return OMX_ErrorBadParameter;
              }
  
              if (profileLevel->nPortIndex != kInputPortIndex) {
                  ALOGE("Invalid port index: %" PRIu32, profileLevel->nPortIndex);
                  return OMX_ErrorUnsupportedIndex;
              }
  
              if (profileLevel->nProfileIndex >= mNumProfileLevels) {
                  return OMX_ErrorNoMore;
              }
  
              profileLevel->eProfile = mProfileLevels[profileLevel->nProfileIndex].mProfile;
              profileLevel->eLevel   = mProfileLevels[profileLevel->nProfileIndex].mLevel;
              return OMX_ErrorNone;
          }
  
          default:
              return SimpleSoftOMXComponent::internalGetParameter(index, params);
      }
  }
  
  OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalSetParameter(
          OMX_INDEXTYPE index, const OMX_PTR params) {
      // Include extension index OMX_INDEXEXTTYPE.
      const int32_t indexFull = index;
  
      switch (indexFull) {
          case OMX_IndexParamStandardComponentRole:
          {
              const OMX_PARAM_COMPONENTROLETYPE *roleParams =
                  (const OMX_PARAM_COMPONENTROLETYPE *)params;
  
              if (!isValidOMXParam(roleParams)) {
                  return OMX_ErrorBadParameter;
              }
  
              if (strncmp((const char *)roleParams->cRole,
                          mComponentRole,
                          OMX_MAX_STRINGNAME_SIZE - 1)) {
                  return OMX_ErrorUndefined;
              }
  
              return OMX_ErrorNone;
          }
  
          case OMX_IndexParamVideoPortFormat:
          {
              OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
                  (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
  
              if (!isValidOMXParam(formatParams)) {
                  return OMX_ErrorBadParameter;
              }
  
              if (formatParams->nPortIndex > kMaxPortIndex) {
                  return OMX_ErrorBadPortIndex;
              }
  
              if (formatParams->nPortIndex == kInputPortIndex) {
                  if (formatParams->eCompressionFormat != mCodingType
                          || formatParams->eColorFormat != OMX_COLOR_FormatUnused) {
                      return OMX_ErrorUnsupportedSetting;
                  }
              } else {
                  if (formatParams->eCompressionFormat != OMX_VIDEO_CodingUnused
                          || formatParams->eColorFormat != OMX_COLOR_FormatYUV420Planar) {
                      return OMX_ErrorUnsupportedSetting;
                  }
              }
  
              return OMX_ErrorNone;
          }
  
          case kPrepareForAdaptivePlaybackIndex:
          {
              const PrepareForAdaptivePlaybackParams* adaptivePlaybackParams =
                      (const PrepareForAdaptivePlaybackParams *)params;
  
              if (!isValidOMXParam(adaptivePlaybackParams)) {
                  return OMX_ErrorBadParameter;
              }
  
              mIsAdaptive = adaptivePlaybackParams->bEnable;
              if (mIsAdaptive) {
                  mAdaptiveMaxWidth = adaptivePlaybackParams->nMaxFrameWidth;
                  mAdaptiveMaxHeight = adaptivePlaybackParams->nMaxFrameHeight;
                  mWidth = mAdaptiveMaxWidth;
                  mHeight = mAdaptiveMaxHeight;
              } else {
                  mAdaptiveMaxWidth = 0;
                  mAdaptiveMaxHeight = 0;
              }
              updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */);
              return OMX_ErrorNone;
          }
  
          case OMX_IndexParamPortDefinition:
          {
              OMX_PARAM_PORTDEFINITIONTYPE *newParams =
                  (OMX_PARAM_PORTDEFINITIONTYPE *)params;
  
              if (!isValidOMXParam(newParams)) {
                  return OMX_ErrorBadParameter;
              }
  
              OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &newParams->format.video;
              OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(newParams->nPortIndex)->mDef;
  
              uint32_t oldWidth = def->format.video.nFrameWidth;
              uint32_t oldHeight = def->format.video.nFrameHeight;
              uint32_t newWidth = video_def->nFrameWidth;
              uint32_t newHeight = video_def->nFrameHeight;
              // We need width, height, stride and slice-height to be non-zero and sensible.
              // These values were chosen to prevent integer overflows further down the line, and do
              // not indicate support for 32kx32k video.
              if (newWidth > 32768 || newHeight > 32768
                      || video_def->nStride > 32768 || video_def->nSliceHeight > 32768) {
                  ALOGE("b/22885421");
                  return OMX_ErrorBadParameter;
              }
              if (newWidth != oldWidth || newHeight != oldHeight) {
                  bool outputPort = (newParams->nPortIndex == kOutputPortIndex);
                  if (outputPort) {
                      // only update (essentially crop) if size changes
                      mWidth = newWidth;
                      mHeight = newHeight;
  
                      updatePortDefinitions(true /* updateCrop */, true /* updateInputSize */);
                      // reset buffer size based on frame size
                      newParams->nBufferSize = def->nBufferSize;
                  } else {
                      // For input port, we only set nFrameWidth and nFrameHeight. Buffer size
                      // is updated when configuring the output port using the max-frame-size,
                      // though client can still request a larger size.
                      def->format.video.nFrameWidth = newWidth;
                      def->format.video.nFrameHeight = newHeight;
                  }
              }
              return SimpleSoftOMXComponent::internalSetParameter(index, params);
          }
  
          default:
              return SimpleSoftOMXComponent::internalSetParameter(index, params);
      }
  }
  
  OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getConfig(
          OMX_INDEXTYPE index, OMX_PTR params) {
      switch ((int)index) {
          case OMX_IndexConfigCommonOutputCrop:
          {
              OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params;
  
              if (!isValidOMXParam(rectParams)) {
                  return OMX_ErrorBadParameter;
              }
  
              if (rectParams->nPortIndex != kOutputPortIndex) {
                  return OMX_ErrorUndefined;
              }
  
              rectParams->nLeft = mCropLeft;
              rectParams->nTop = mCropTop;
              rectParams->nWidth = mCropWidth;
              rectParams->nHeight = mCropHeight;
  
              return OMX_ErrorNone;
          }
          case kDescribeColorAspectsIndex:
          {
              if (!supportsDescribeColorAspects()) {
                  return OMX_ErrorUnsupportedIndex;
              }
  
              DescribeColorAspectsParams* colorAspectsParams =
                      (DescribeColorAspectsParams *)params;
  
              if (!isValidOMXParam(colorAspectsParams)) {
                  return OMX_ErrorBadParameter;
              }
  
              if (colorAspectsParams->nPortIndex != kOutputPortIndex) {
                  return OMX_ErrorBadParameter;
              }
  
              colorAspectsParams->sAspects = mFinalColorAspects;
              if (colorAspectsParams->bRequestingDataSpace || colorAspectsParams->bDataSpaceChanged) {
                  return OMX_ErrorUnsupportedSetting;
              }
  
              return OMX_ErrorNone;
          }
  
          case kDescribeHdrStaticInfoIndex:
          {
              if (!supportDescribeHdrStaticInfo()) {
                  return OMX_ErrorUnsupportedIndex;
              }
  
              DescribeHDRStaticInfoParams* hdrStaticInfoParams =
                      (DescribeHDRStaticInfoParams *)params;
  
              if (!isValidOMXParam(hdrStaticInfoParams)) {
                  return OMX_ErrorBadParameter;
              }
  
              if (hdrStaticInfoParams->nPortIndex != kOutputPortIndex) {
                  return OMX_ErrorBadPortIndex;
              }
  
              hdrStaticInfoParams->sInfo = mHdrStaticInfo;
  
              return OMX_ErrorNone;
          }
  
          default:
              return OMX_ErrorUnsupportedIndex;
      }
  }
  
  OMX_ERRORTYPE SoftVideoDecoderOMXComponent::setConfig(
          OMX_INDEXTYPE index, const OMX_PTR params){
      switch ((int)index) {
          case kDescribeColorAspectsIndex:
          {
              if (!supportsDescribeColorAspects()) {
                  return OMX_ErrorUnsupportedIndex;
              }
              const DescribeColorAspectsParams* colorAspectsParams =
                      (const DescribeColorAspectsParams *)params;
  
              if (!isValidOMXParam(colorAspectsParams)) {
                  return OMX_ErrorBadParameter;
              }
  
              if (colorAspectsParams->nPortIndex != kOutputPortIndex) {
                  return OMX_ErrorBadParameter;
              }
  
              // Update color aspects if necessary.
              if (colorAspectsDiffer(colorAspectsParams->sAspects, mDefaultColorAspects)) {
                  mDefaultColorAspects = colorAspectsParams->sAspects;
                  status_t err = handleColorAspectsChange();
                  CHECK(err == OK);
              }
              return OMX_ErrorNone;
          }
  
          case kDescribeHdrStaticInfoIndex:
          {
              if (!supportDescribeHdrStaticInfo()) {
                  return OMX_ErrorUnsupportedIndex;
              }
  
              const DescribeHDRStaticInfoParams* hdrStaticInfoParams =
                      (DescribeHDRStaticInfoParams *)params;
  
              if (!isValidOMXParam(hdrStaticInfoParams)) {
                  return OMX_ErrorBadParameter;
              }
  
              if (hdrStaticInfoParams->nPortIndex != kOutputPortIndex) {
                  return OMX_ErrorBadPortIndex;
              }
  
              mHdrStaticInfo = hdrStaticInfoParams->sInfo;
              updatePortDefinitions(false);
  
              return OMX_ErrorNone;
          }
  
          default:
              return OMX_ErrorUnsupportedIndex;
      }
  }
  
  
  OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getExtensionIndex(
          const char *name, OMX_INDEXTYPE *index) {
      if (!strcmp(name, "OMX.google.android.index.prepareForAdaptivePlayback")) {
          *(int32_t*)index = kPrepareForAdaptivePlaybackIndex;
          return OMX_ErrorNone;
      } else if (!strcmp(name, "OMX.google.android.index.describeColorAspects")
                  && supportsDescribeColorAspects()) {
          *(int32_t*)index = kDescribeColorAspectsIndex;
          return OMX_ErrorNone;
      } else if (!strcmp(name, "OMX.google.android.index.describeHDRStaticInfo")
              && supportDescribeHdrStaticInfo()) {
          *(int32_t*)index = kDescribeHdrStaticInfoIndex;
          return OMX_ErrorNone;
      }
  
      return SimpleSoftOMXComponent::getExtensionIndex(name, index);
  }
  
  bool SoftVideoDecoderOMXComponent::supportsDescribeColorAspects() {
      return getColorAspectPreference() != kNotSupported;
  }
  
  int SoftVideoDecoderOMXComponent::getColorAspectPreference() {
      return kNotSupported;
  }
  
  bool SoftVideoDecoderOMXComponent::supportDescribeHdrStaticInfo() {
      return false;
  }
  
  void SoftVideoDecoderOMXComponent::onReset() {
      mOutputPortSettingsChange = NONE;
  }
  
  void SoftVideoDecoderOMXComponent::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
      if (portIndex != kOutputPortIndex) {
          return;
      }
  
      switch (mOutputPortSettingsChange) {
          case NONE:
              break;
  
          case AWAITING_DISABLED:
          {
              CHECK(!enabled);
              mOutputPortSettingsChange = AWAITING_ENABLED;
              break;
          }
  
          default:
          {
              CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
              CHECK(enabled);
              mOutputPortSettingsChange = NONE;
              break;
          }
      }
  }
  
  }  // namespace android
  