diff options
Diffstat (limited to 'dom/media/platforms/MediaCodecsSupport.cpp')
-rw-r--r-- | dom/media/platforms/MediaCodecsSupport.cpp | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/dom/media/platforms/MediaCodecsSupport.cpp b/dom/media/platforms/MediaCodecsSupport.cpp new file mode 100644 index 0000000000..13c10ab389 --- /dev/null +++ b/dom/media/platforms/MediaCodecsSupport.cpp @@ -0,0 +1,331 @@ +/* -*- 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 <array> + +#ifdef MOZ_AV1 +# include "AOMDecoder.h" +#endif +#include "MediaCodecsSupport.h" +#include "MP4Decoder.h" +#include "PlatformDecoderModule.h" +#include "TheoraDecoder.h" +#include "VPXDecoder.h" +#include "mozilla/AppShutdown.h" +#include "mozilla/gfx/gfxVars.h" +#include "nsTHashMap.h" +#include "VideoUtils.h" + +using MediaCodecsSupport = mozilla::media::MediaCodecsSupport; + +namespace mozilla::media { + +static StaticAutoPtr<MCSInfo> sInstance; +static StaticMutex sInitMutex; +static StaticMutex sUpdateMutex; + +#define CODEC_SUPPORT_LOG(msg, ...) \ + MOZ_LOG(sPDMLog, LogLevel::Debug, ("MediaCodecsSupport, " msg, ##__VA_ARGS__)) + +void MCSInfo::AddSupport(const MediaCodecsSupported& aSupport) { + StaticMutexAutoLock lock(sUpdateMutex); + MCSInfo* instance = GetInstance(); + if (!instance) { + CODEC_SUPPORT_LOG("Can't add codec support without a MCSInfo instance!"); + return; + } + instance->mSupport += aSupport; +} + +MediaCodecsSupported MCSInfo::GetSupport() { + StaticMutexAutoLock lock(sUpdateMutex); + MCSInfo* instance = GetInstance(); + if (!instance) { + CODEC_SUPPORT_LOG("Can't get codec support without a MCSInfo instance!"); + return MediaCodecsSupported{}; + } + return instance->mSupport; +} + +void MCSInfo::ResetSupport() { + StaticMutexAutoLock lock(sUpdateMutex); + MCSInfo* instance = GetInstance(); + if (!instance) { + CODEC_SUPPORT_LOG("Can't reset codec support without a MCSInfo instance!"); + return; + } + instance->mSupport.clear(); +} + +DecodeSupportSet MCSInfo::GetDecodeSupportSet( + const MediaCodec& aCodec, const MediaCodecsSupported& aSupported) { + DecodeSupportSet support; + const auto supportInfo = GetCodecDefinition(aCodec); + if (aSupported.contains(supportInfo.swDecodeSupport)) { + support += DecodeSupport::SoftwareDecode; + } + if (aSupported.contains(supportInfo.hwDecodeSupport)) { + support += DecodeSupport::HardwareDecode; + } + return support; +} + +MediaCodecsSupported MCSInfo::GetDecodeMediaCodecsSupported( + const MediaCodec& aCodec, const DecodeSupportSet& aSupportSet) { + MediaCodecsSupported support; + const auto supportInfo = GetCodecDefinition(aCodec); + if (aSupportSet.contains(DecodeSupport::SoftwareDecode)) { + support += supportInfo.swDecodeSupport; + } + if (aSupportSet.contains(DecodeSupport::HardwareDecode)) { + support += supportInfo.hwDecodeSupport; + } + if (aSupportSet.contains(DecodeSupport::UnsureDueToLackOfExtension)) { + support += supportInfo.lackOfHWExtenstion; + } + return support; +} + +bool MCSInfo::SupportsSoftwareDecode( + const MediaCodecsSupported& aSupportedCodecs, const MediaCodec& aCodec) { + return ( + aSupportedCodecs.contains(GetCodecDefinition(aCodec).swDecodeSupport)); +} + +bool MCSInfo::SupportsHardwareDecode( + const MediaCodecsSupported& aSupportedCodecs, const MediaCodec& aCodec) { + return ( + aSupportedCodecs.contains(GetCodecDefinition(aCodec).hwDecodeSupport)); +} + +void MCSInfo::GetMediaCodecsSupportedString( + nsCString& aSupportString, const MediaCodecsSupported& aSupportedCodecs) { + CodecDefinition supportInfo; + aSupportString = ""_ns; + MCSInfo* instance = GetInstance(); + if (!instance) { + CODEC_SUPPORT_LOG("Can't get codec support string w/o a MCSInfo instance!"); + return; + } + for (const auto& it : GetAllCodecDefinitions()) { + if (it.codec == MediaCodec::SENTINEL) { + break; + } + if (!instance->mHashTableCodec->Get(it.codec, &supportInfo)) { + CODEC_SUPPORT_LOG("Can't find codec for MediaCodecsSupported enum: %d", + static_cast<int>(it.codec)); + continue; + } + aSupportString.Append(supportInfo.commonName); + bool foundSupport = false; + if (aSupportedCodecs.contains(it.swDecodeSupport)) { + aSupportString.Append(" SW"_ns); + foundSupport = true; + } + if (aSupportedCodecs.contains(it.hwDecodeSupport)) { + aSupportString.Append(" HW"_ns); + foundSupport = true; + } + if (aSupportedCodecs.contains(it.lackOfHWExtenstion)) { + aSupportString.Append(" LACK_OF_EXTENSION"_ns); + foundSupport = true; + } + if (!foundSupport) { + aSupportString.Append(" NONE"_ns); + } + aSupportString.Append("\n"_ns); + } + // Remove any trailing newline characters + if (!aSupportString.IsEmpty()) { + aSupportString.Truncate(aSupportString.Length() - 1); + } +} + +MCSInfo* MCSInfo::GetInstance() { + StaticMutexAutoLock lock(sInitMutex); + if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { + CODEC_SUPPORT_LOG("In XPCOM shutdown - not returning MCSInfo instance!"); + return nullptr; + } + if (!sInstance) { + sInstance = new MCSInfo(); + } + return sInstance.get(); +} + +MCSInfo::MCSInfo() { + // Initialize hash tables + mHashTableMCS.reset(new nsTHashMap<MediaCodecsSupport, CodecDefinition>()); + mHashTableCodec.reset(new nsTHashMap<MediaCodec, CodecDefinition>()); + + for (const auto& it : GetAllCodecDefinitions()) { + // Insert MediaCodecsSupport values as keys + mHashTableMCS->InsertOrUpdate(it.swDecodeSupport, it); + mHashTableMCS->InsertOrUpdate(it.hwDecodeSupport, it); + // Insert codec enum values as keys + mHashTableCodec->InsertOrUpdate(it.codec, it); + } + + GetMainThreadSerialEventTarget()->Dispatch( + NS_NewRunnableFunction("MCSInfo::MCSInfo", [&] { + // Ensure hash tables freed on shutdown + RunOnShutdown( + [&] { + mHashTableMCS.reset(); + mHashTableString.reset(); + mHashTableCodec.reset(); + sInstance = nullptr; + }, + ShutdownPhase::XPCOMShutdown); + })); +} + +CodecDefinition MCSInfo::GetCodecDefinition(const MediaCodec& aCodec) { + CodecDefinition info; + MCSInfo* instance = GetInstance(); + if (!instance) { + CODEC_SUPPORT_LOG("Can't get codec definition without a MCSInfo instance!"); + } else if (!instance->mHashTableCodec->Get(aCodec, &info)) { + CODEC_SUPPORT_LOG("Could not find codec definition for codec enum: %d!", + static_cast<int>(aCodec)); + } + return info; +} + +MediaCodecsSupport MCSInfo::GetMediaCodecsSupportEnum( + const MediaCodec& aCodec, const DecodeSupportSet& aSupport) { + if (aSupport.isEmpty()) { + return MediaCodecsSupport{}; + } + const CodecDefinition cd = GetCodecDefinition(aCodec); + if (aSupport.contains(DecodeSupport::SoftwareDecode)) { + return cd.swDecodeSupport; + } + if (aSupport.contains(DecodeSupport::HardwareDecode)) { + return cd.hwDecodeSupport; + } + return MediaCodecsSupport::SENTINEL; +} + +MediaCodecSet MCSInfo::GetMediaCodecSetFromMimeTypes( + const nsTArray<nsCString>& aCodecStrings) { + MediaCodecSet support; + for (const auto& ms : aCodecStrings) { + const MediaCodec codec = MCSInfo::GetMediaCodecFromMimeType(ms); + if (codec == MediaCodec::SENTINEL) { + continue; + } + MOZ_ASSERT(codec < MediaCodec::SENTINEL); + support += codec; + } + return support; +} + +MediaCodec MCSInfo::GetMediaCodecFromMimeType(const nsACString& aMimeType) { + // Video codecs + if (MP4Decoder::IsH264(aMimeType)) { + return MediaCodec::H264; + } + if (VPXDecoder::IsVP8(aMimeType)) { + return MediaCodec::VP8; + } + if (VPXDecoder::IsVP9(aMimeType)) { + return MediaCodec::VP9; + } + if (TheoraDecoder::IsTheora(aMimeType)) { + return MediaCodec::Theora; + } + if (MP4Decoder::IsHEVC(aMimeType)) { + return MediaCodec::HEVC; + } +#ifdef MOZ_AV1 + if (AOMDecoder::IsAV1(aMimeType)) { + return MediaCodec::AV1; + } + if (aMimeType.EqualsLiteral("video/av01")) { + return MediaCodec::AV1; + } +#endif + // TODO: Should this be Android only? +#ifdef ANDROID + if (aMimeType.EqualsLiteral("video/x-vnd.on2.vp8")) { + return MediaCodec::VP8; + } + if (aMimeType.EqualsLiteral("video/x-vnd.on2.vp9")) { + return MediaCodec::VP9; + } +#endif + // Audio codecs + if (MP4Decoder::IsAAC(aMimeType)) { + return MediaCodec::AAC; + } + if (aMimeType.EqualsLiteral("audio/vorbis")) { + return MediaCodec::Vorbis; + } + if (aMimeType.EqualsLiteral("audio/flac")) { + return MediaCodec::FLAC; + } + if (IsWaveMimetype(aMimeType)) { + return MediaCodec::Wave; + } + if (aMimeType.EqualsLiteral("audio/opus")) { + return MediaCodec::Opus; + } + if (aMimeType.EqualsLiteral("audio/mpeg")) { + return MediaCodec::MP3; + } + + CODEC_SUPPORT_LOG("No specific codec enum for MIME type string: %s", + nsCString(aMimeType).get()); + return MediaCodec::SENTINEL; +} + +std::array<CodecDefinition, 13> MCSInfo::GetAllCodecDefinitions() { + static constexpr std::array<CodecDefinition, 13> codecDefinitions = { + {{MediaCodec::H264, "H264", "video/avc", + MediaCodecsSupport::H264SoftwareDecode, + MediaCodecsSupport::H264HardwareDecode, MediaCodecsSupport::SENTINEL}, + {MediaCodec::VP9, "VP9", "video/vp9", + MediaCodecsSupport::VP9SoftwareDecode, + MediaCodecsSupport::VP9HardwareDecode, MediaCodecsSupport::SENTINEL}, + {MediaCodec::VP8, "VP8", "video/vp8", + MediaCodecsSupport::VP8SoftwareDecode, + MediaCodecsSupport::VP8HardwareDecode, MediaCodecsSupport::SENTINEL}, + {MediaCodec::AV1, "AV1", "video/av1", + MediaCodecsSupport::AV1SoftwareDecode, + MediaCodecsSupport::AV1HardwareDecode, + MediaCodecsSupport::AV1LackOfExtension}, + {MediaCodec::HEVC, "HEVC", "video/hevc", + MediaCodecsSupport::HEVCSoftwareDecode, + MediaCodecsSupport::HEVCHardwareDecode, MediaCodecsSupport::SENTINEL}, + {MediaCodec::Theora, "Theora", "video/theora", + MediaCodecsSupport::TheoraSoftwareDecode, + MediaCodecsSupport::TheoraHardwareDecode, MediaCodecsSupport::SENTINEL}, + {MediaCodec::AAC, "AAC", "audio/mp4a-latm", + MediaCodecsSupport::AACSoftwareDecode, + MediaCodecsSupport::AACHardwareDecode, MediaCodecsSupport::SENTINEL}, + {MediaCodec::MP3, "MP3", "audio/mpeg", + MediaCodecsSupport::MP3SoftwareDecode, + MediaCodecsSupport::MP3HardwareDecode, MediaCodecsSupport::SENTINEL}, + {MediaCodec::Opus, "Opus", "audio/opus", + MediaCodecsSupport::OpusSoftwareDecode, + MediaCodecsSupport::OpusHardwareDecode, MediaCodecsSupport::SENTINEL}, + {MediaCodec::Vorbis, "Vorbis", "audio/vorbis", + MediaCodecsSupport::VorbisSoftwareDecode, + MediaCodecsSupport::VorbisHardwareDecode, MediaCodecsSupport::SENTINEL}, + {MediaCodec::FLAC, "FLAC", "audio/flac", + MediaCodecsSupport::FLACSoftwareDecode, + MediaCodecsSupport::FLACHardwareDecode, MediaCodecsSupport::SENTINEL}, + {MediaCodec::Wave, "Wave", "audio/x-wav", + MediaCodecsSupport::WaveSoftwareDecode, + MediaCodecsSupport::WaveHardwareDecode, MediaCodecsSupport::SENTINEL}, + {MediaCodec::SENTINEL, "Undefined codec name", + "Undefined MIME type string", MediaCodecsSupport::SENTINEL, + MediaCodecsSupport::SENTINEL}}}; + return codecDefinitions; +} +} // namespace mozilla::media + +#undef CODEC_SUPPORT_LOG |