gecko-dev/dom/media/platforms/omx/OmxPlatformLayer.cpp

328 строки
9.8 KiB
C++

/* -*- 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 "OMX_VideoExt.h" // For VP8.
#if defined(MOZ_WIDGET_GONK) && (ANDROID_VERSION == 20 || ANDROID_VERSION == 19)
#define OMX_PLATFORM_GONK
#include "GonkOmxPlatformLayer.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<typename ParamType>
class OmxConfig
{
public:
virtual ~OmxConfig() {}
// Subclasses should implement this method to configure the codec.
virtual OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const ParamType& aParam) = 0;
};
typedef OmxConfig<AudioInfo> OmxAudioConfig;
typedef OmxConfig<VideoInfo> OmxVideoConfig;
template<typename ConfigType>
UniquePtr<ConfigType> ConfigForMime(const nsACString&);
static OMX_ERRORTYPE
ConfigAudioOutputPort(OmxPlatformLayer& aOmx, const AudioInfo& aInfo)
{
OMX_ERRORTYPE err;
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOmxParameter(&def);
def.nPortIndex = aOmx.OutputPortIndex();
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_ERRORTYPE err;
OMX_AUDIO_PARAM_AACPROFILETYPE aacProfile;
InitOmxParameter(&aacProfile);
aacProfile.nPortIndex = aOmx.InputPortIndex();
err = aOmx.GetParameter(OMX_IndexParamAudioAac, &aacProfile, sizeof(aacProfile));
RETURN_IF_ERR(err);
aacProfile.nChannels = aInfo.mChannels;
aacProfile.nSampleRate = aInfo.mRate;
aacProfile.eAACProfile = static_cast<OMX_AUDIO_AACPROFILETYPE>(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_ERRORTYPE err;
OMX_AUDIO_PARAM_MP3TYPE mp3Param;
InitOmxParameter(&mp3Param);
mp3Param.nPortIndex = aOmx.InputPortIndex();
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 <OmxAmrSampleRate R>
class OmxAmrConfig : public OmxAudioConfig
{
public:
OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const AudioInfo& aInfo) override
{
OMX_ERRORTYPE err;
OMX_AUDIO_PARAM_AMRTYPE def;
InitOmxParameter(&def);
def.nPortIndex = aOmx.InputPortIndex();
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<OmxAudioConfig>
ConfigForMime(const nsACString& aMimeType)
{
UniquePtr<OmxAudioConfig> 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<OmxAmrSampleRate::kNarrowBand>());
} else if (aMimeType.EqualsLiteral("audio/amr-wb")) {
conf.reset(new OmxAmrConfig<OmxAmrSampleRate::kWideBand>());
}
}
return Move(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_PARAM_PORTDEFINITIONTYPE def;
// Set up in/out port definition.
nsTArray<uint32_t> 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<OmxVideoConfig>
ConfigForMime(const nsACString& aMimeType)
{
UniquePtr<OmxVideoConfig> conf;
if (OmxPlatformLayer::SupportsMimeType(aMimeType)) {
conf.reset(new OmxCommonVideoConfig());
}
return Move(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<OmxAudioConfig> conf(ConfigForMime<OmxAudioConfig>(mInfo->mMimeType));
MOZ_ASSERT(conf.get());
return conf->Apply(*this, *(mInfo->GetAsAudioInfo()));
} else if (mInfo->IsVideo()) {
GetParameter(OMX_IndexParamVideoInit, &portParam, sizeof(portParam));
UniquePtr<OmxVideoConfig> conf(ConfigForMime<OmxVideoConfig>(mInfo->mMimeType));
MOZ_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_CODINGTYPE>(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.
#ifdef OMX_PLATFORM_GONK
bool
OmxPlatformLayer::SupportsMimeType(const nsACString& aMimeType)
{
return GonkOmxPlatformLayer::FindComponents(aMimeType);
}
OmxPlatformLayer*
OmxPlatformLayer::Create(OmxDataDecoder* aDataDecoder,
OmxPromiseLayer* aPromiseLayer,
TaskQueue* aTaskQueue,
layers::ImageContainer* aImageContainer)
{
return new GonkOmxPlatformLayer(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
}