/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "OmxPlatformLayer.h" #include "OmxDataDecoder.h" #include "OMX_Component.h" #include "OMX_VideoExt.h" // For VP8. #ifdef MOZ_OMX # include "PureOmxPlatformLayer.h" #endif #include "VPXDecoder.h" #ifdef LOG # undef LOG #endif #define LOG(arg, ...) \ MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, \ ("OmxPlatformLayer -- %s: " arg, __func__, ##__VA_ARGS__)) #define RETURN_IF_ERR(err) \ if (err != OMX_ErrorNone) { \ LOG("error: 0x%08x", err); \ return err; \ } // Common OMX decoder configuration code. namespace mozilla { // This helper class encapsulates the details of component parameters setting // for different OMX audio & video codecs. template class OmxConfig { public: virtual ~OmxConfig() = default; // Subclasses should implement this method to configure the codec. virtual OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const ParamType& aParam) = 0; }; typedef OmxConfig OmxAudioConfig; typedef OmxConfig OmxVideoConfig; template UniquePtr ConfigForMime(const nsACString&); static OMX_ERRORTYPE ConfigAudioOutputPort(OmxPlatformLayer& aOmx, const AudioInfo& aInfo) { OMX_PARAM_PORTDEFINITIONTYPE def; InitOmxParameter(&def); def.nPortIndex = aOmx.OutputPortIndex(); OMX_ERRORTYPE err = aOmx.GetParameter(OMX_IndexParamPortDefinition, &def, sizeof(def)); RETURN_IF_ERR(err); def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; err = aOmx.SetParameter(OMX_IndexParamPortDefinition, &def, sizeof(def)); RETURN_IF_ERR(err); OMX_AUDIO_PARAM_PCMMODETYPE pcmParams; InitOmxParameter(&pcmParams); pcmParams.nPortIndex = def.nPortIndex; err = aOmx.GetParameter(OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams)); RETURN_IF_ERR(err); pcmParams.nChannels = aInfo.mChannels; pcmParams.eNumData = OMX_NumericalDataSigned; pcmParams.bInterleaved = OMX_TRUE; pcmParams.nBitPerSample = 16; pcmParams.nSamplingRate = aInfo.mRate; pcmParams.ePCMMode = OMX_AUDIO_PCMModeLinear; err = aOmx.SetParameter(OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams)); RETURN_IF_ERR(err); LOG("Config OMX_IndexParamAudioPcm, channel %lu, sample rate %lu", pcmParams.nChannels, pcmParams.nSamplingRate); return OMX_ErrorNone; } class OmxAacConfig : public OmxAudioConfig { public: OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const AudioInfo& aInfo) override { OMX_AUDIO_PARAM_AACPROFILETYPE aacProfile; InitOmxParameter(&aacProfile); aacProfile.nPortIndex = aOmx.InputPortIndex(); OMX_ERRORTYPE err = aOmx.GetParameter(OMX_IndexParamAudioAac, &aacProfile, sizeof(aacProfile)); RETURN_IF_ERR(err); aacProfile.nChannels = aInfo.mChannels; aacProfile.nSampleRate = aInfo.mRate; aacProfile.eAACProfile = static_cast(aInfo.mProfile); err = aOmx.SetParameter(OMX_IndexParamAudioAac, &aacProfile, sizeof(aacProfile)); RETURN_IF_ERR(err); LOG("Config OMX_IndexParamAudioAac, channel %lu, sample rate %lu, profile " "%d", aacProfile.nChannels, aacProfile.nSampleRate, aacProfile.eAACProfile); return ConfigAudioOutputPort(aOmx, aInfo); } }; class OmxMp3Config : public OmxAudioConfig { public: OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const AudioInfo& aInfo) override { OMX_AUDIO_PARAM_MP3TYPE mp3Param; InitOmxParameter(&mp3Param); mp3Param.nPortIndex = aOmx.InputPortIndex(); OMX_ERRORTYPE err = aOmx.GetParameter(OMX_IndexParamAudioMp3, &mp3Param, sizeof(mp3Param)); RETURN_IF_ERR(err); mp3Param.nChannels = aInfo.mChannels; mp3Param.nSampleRate = aInfo.mRate; err = aOmx.SetParameter(OMX_IndexParamAudioMp3, &mp3Param, sizeof(mp3Param)); RETURN_IF_ERR(err); LOG("Config OMX_IndexParamAudioMp3, channel %lu, sample rate %lu", mp3Param.nChannels, mp3Param.nSampleRate); return ConfigAudioOutputPort(aOmx, aInfo); } }; enum OmxAmrSampleRate { kNarrowBand = 8000, kWideBand = 16000, }; template class OmxAmrConfig : public OmxAudioConfig { public: OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const AudioInfo& aInfo) override { OMX_AUDIO_PARAM_AMRTYPE def; InitOmxParameter(&def); def.nPortIndex = aOmx.InputPortIndex(); OMX_ERRORTYPE err = aOmx.GetParameter(OMX_IndexParamAudioAmr, &def, sizeof(def)); RETURN_IF_ERR(err); def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; err = aOmx.SetParameter(OMX_IndexParamAudioAmr, &def, sizeof(def)); RETURN_IF_ERR(err); MOZ_ASSERT(aInfo.mChannels == 1); MOZ_ASSERT(aInfo.mRate == R); return ConfigAudioOutputPort(aOmx, aInfo); } }; template <> UniquePtr ConfigForMime(const nsACString& aMimeType) { UniquePtr conf; if (OmxPlatformLayer::SupportsMimeType(aMimeType)) { if (aMimeType.EqualsLiteral("audio/mp4a-latm")) { conf.reset(new OmxAacConfig()); } else if (aMimeType.EqualsLiteral("audio/mp3") || aMimeType.EqualsLiteral("audio/mpeg")) { conf.reset(new OmxMp3Config()); } else if (aMimeType.EqualsLiteral("audio/3gpp")) { conf.reset(new OmxAmrConfig()); } else if (aMimeType.EqualsLiteral("audio/amr-wb")) { conf.reset(new OmxAmrConfig()); } } return conf; } // There should be a better way to calculate it. #define MIN_VIDEO_INPUT_BUFFER_SIZE 64 * 1024 class OmxCommonVideoConfig : public OmxVideoConfig { public: explicit OmxCommonVideoConfig() : OmxVideoConfig() {} OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const VideoInfo& aInfo) override { OMX_ERRORTYPE err = OMX_ErrorNone; OMX_PARAM_PORTDEFINITIONTYPE def; // Set up in/out port definition. nsTArray ports; aOmx.GetPortIndices(ports); for (auto idx : ports) { InitOmxParameter(&def); def.nPortIndex = idx; err = aOmx.GetParameter(OMX_IndexParamPortDefinition, &def, sizeof(def)); RETURN_IF_ERR(err); def.format.video.nFrameWidth = aInfo.mDisplay.width; def.format.video.nFrameHeight = aInfo.mDisplay.height; def.format.video.nStride = aInfo.mImage.width; def.format.video.nSliceHeight = aInfo.mImage.height; if (def.eDir == OMX_DirInput) { def.format.video.eCompressionFormat = aOmx.CompressionFormat(); def.format.video.eColorFormat = OMX_COLOR_FormatUnused; if (def.nBufferSize < MIN_VIDEO_INPUT_BUFFER_SIZE) { def.nBufferSize = aInfo.mImage.width * aInfo.mImage.height; LOG("Change input buffer size to %lu", def.nBufferSize); } } else { def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; } err = aOmx.SetParameter(OMX_IndexParamPortDefinition, &def, sizeof(def)); } return err; } }; template <> UniquePtr ConfigForMime(const nsACString& aMimeType) { UniquePtr conf; if (OmxPlatformLayer::SupportsMimeType(aMimeType)) { conf.reset(new OmxCommonVideoConfig()); } return conf; } OMX_ERRORTYPE OmxPlatformLayer::Config() { MOZ_ASSERT(mInfo); OMX_PORT_PARAM_TYPE portParam; InitOmxParameter(&portParam); if (mInfo->IsAudio()) { GetParameter(OMX_IndexParamAudioInit, &portParam, sizeof(portParam)); mStartPortNumber = portParam.nStartPortNumber; UniquePtr conf( ConfigForMime(mInfo->mMimeType)); MOZ_RELEASE_ASSERT(conf.get()); return conf->Apply(*this, *(mInfo->GetAsAudioInfo())); } else if (mInfo->IsVideo()) { GetParameter(OMX_IndexParamVideoInit, &portParam, sizeof(portParam)); UniquePtr conf( ConfigForMime(mInfo->mMimeType)); MOZ_RELEASE_ASSERT(conf.get()); return conf->Apply(*this, *(mInfo->GetAsVideoInfo())); } else { MOZ_ASSERT_UNREACHABLE("non-AV data (text?) is not supported."); return OMX_ErrorNotImplemented; } } OMX_VIDEO_CODINGTYPE OmxPlatformLayer::CompressionFormat() { MOZ_ASSERT(mInfo); if (mInfo->mMimeType.EqualsLiteral("video/avc")) { return OMX_VIDEO_CodingAVC; } else if (mInfo->mMimeType.EqualsLiteral("video/mp4v-es") || mInfo->mMimeType.EqualsLiteral("video/mp4")) { return OMX_VIDEO_CodingMPEG4; } else if (mInfo->mMimeType.EqualsLiteral("video/3gpp")) { return OMX_VIDEO_CodingH263; } else if (VPXDecoder::IsVP8(mInfo->mMimeType)) { return static_cast(OMX_VIDEO_CodingVP8); } else { MOZ_ASSERT_UNREACHABLE("Unsupported compression format"); return OMX_VIDEO_CodingUnused; } } // Implementations for different platforms will be defined in their own files. #if defined(MOZ_OMX) bool OmxPlatformLayer::SupportsMimeType(const nsACString& aMimeType) { return PureOmxPlatformLayer::SupportsMimeType(aMimeType); } OmxPlatformLayer* OmxPlatformLayer::Create( OmxDataDecoder* aDataDecoder, OmxPromiseLayer* aPromiseLayer, TaskQueue* aTaskQueue, layers::ImageContainer* aImageContainer) { return new PureOmxPlatformLayer(aDataDecoder, aPromiseLayer, aTaskQueue, aImageContainer); } #else // For platforms without OMX IL support. bool OmxPlatformLayer::SupportsMimeType(const nsACString& aMimeType) { return false; } OmxPlatformLayer* OmxPlatformLayer::Create( OmxDataDecoder* aDataDecoder, OmxPromiseLayer* aPromiseLayer, TaskQueue* aTaskQueue, layers::ImageContainer* aImageContainer) { return nullptr; } #endif } // namespace mozilla