summaryrefslogtreecommitdiffstats
path: root/dom/media/platforms/PDMFactory.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/platforms/PDMFactory.cpp')
-rw-r--r--dom/media/platforms/PDMFactory.cpp904
1 files changed, 904 insertions, 0 deletions
diff --git a/dom/media/platforms/PDMFactory.cpp b/dom/media/platforms/PDMFactory.cpp
new file mode 100644
index 0000000000..49d9e835f7
--- /dev/null
+++ b/dom/media/platforms/PDMFactory.cpp
@@ -0,0 +1,904 @@
+/* -*- 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 "PDMFactory.h"
+
+#ifdef MOZ_AV1
+# include "AOMDecoder.h"
+#endif
+#include "AgnosticDecoderModule.h"
+#include "AudioTrimmer.h"
+#include "BlankDecoderModule.h"
+#include "DecoderDoctorDiagnostics.h"
+#include "EMEDecoderModule.h"
+#include "GMPDecoderModule.h"
+#include "H264.h"
+#include "MP4Decoder.h"
+#include "MediaChangeMonitor.h"
+#include "MediaInfo.h"
+#include "OpusDecoder.h"
+#include "TheoraDecoder.h"
+#include "VPXDecoder.h"
+#include "VideoUtils.h"
+#include "VorbisDecoder.h"
+#include "WAVDecoder.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/RemoteDecodeUtils.h"
+#include "mozilla/RemoteDecoderManagerChild.h"
+#include "mozilla/RemoteDecoderModule.h"
+#include "mozilla/SharedThreadPool.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/SyncRunnable.h"
+#include "mozilla/TaskQueue.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "nsIXULRuntime.h" // for BrowserTabsRemoteAutostart
+#include "nsPrintfCString.h"
+
+#include "mozilla/ipc/UtilityAudioDecoderParent.h"
+
+#ifdef XP_WIN
+# include "WMFDecoderModule.h"
+# include "mozilla/WindowsVersion.h"
+# ifdef MOZ_WMF_MEDIA_ENGINE
+# include "MFMediaEngineDecoderModule.h"
+# endif
+# ifdef MOZ_WMF_CDM
+# include "mozilla/CDMProxy.h"
+# endif
+#endif
+#ifdef MOZ_FFVPX
+# include "FFVPXRuntimeLinker.h"
+#endif
+#ifdef MOZ_FFMPEG
+# include "FFmpegRuntimeLinker.h"
+#endif
+#ifdef MOZ_APPLEMEDIA
+# include "AppleDecoderModule.h"
+#endif
+#ifdef MOZ_WIDGET_ANDROID
+# include "AndroidDecoderModule.h"
+#endif
+#ifdef MOZ_OMX
+# include "OmxDecoderModule.h"
+#endif
+
+#include <functional>
+
+using DecodeSupport = mozilla::media::DecodeSupport;
+using DecodeSupportSet = mozilla::media::DecodeSupportSet;
+using MediaCodec = mozilla::media::MediaCodec;
+using MediaCodecsSupport = mozilla::media::MediaCodecsSupport;
+using MediaCodecsSupported = mozilla::media::MediaCodecsSupported;
+using MCSInfo = mozilla::media::MCSInfo;
+
+namespace mozilla {
+
+#define PDM_INIT_LOG(msg, ...) \
+ MOZ_LOG(sPDMLog, LogLevel::Debug, ("PDMInitializer, " msg, ##__VA_ARGS__))
+
+extern already_AddRefed<PlatformDecoderModule> CreateNullDecoderModule();
+
+class PDMInitializer final {
+ public:
+ // This function should only be executed ONCE per process.
+ static void InitPDMs();
+
+ // Return true if we've finished PDMs initialization.
+ static bool HasInitializedPDMs();
+
+ private:
+ static void InitGpuPDMs() {
+#ifdef XP_WIN
+ if (!IsWin7AndPre2000Compatible()) {
+ WMFDecoderModule::Init();
+ }
+#endif
+ }
+
+ static void InitRddPDMs() {
+#ifdef XP_WIN
+ if (!IsWin7AndPre2000Compatible()) {
+ WMFDecoderModule::Init();
+ }
+#endif
+#ifdef MOZ_APPLEMEDIA
+ AppleDecoderModule::Init();
+#endif
+#ifdef MOZ_FFVPX
+ FFVPXRuntimeLinker::Init();
+#endif
+#ifdef MOZ_FFMPEG
+ if (StaticPrefs::media_rdd_ffmpeg_enabled()) {
+ FFmpegRuntimeLinker::Init();
+ }
+#endif
+ }
+
+ static void InitUtilityPDMs() {
+ const ipc::SandboxingKind kind = GetCurrentSandboxingKind();
+#ifdef XP_WIN
+ if (!IsWin7AndPre2000Compatible() &&
+ kind == ipc::SandboxingKind::UTILITY_AUDIO_DECODING_WMF) {
+ WMFDecoderModule::Init();
+ }
+# ifdef MOZ_WMF_MEDIA_ENGINE
+ if (IsWin10OrLater() && StaticPrefs::media_wmf_media_engine_enabled() &&
+ kind == ipc::SandboxingKind::MF_MEDIA_ENGINE_CDM) {
+ MFMediaEngineDecoderModule::Init();
+ }
+# endif
+#endif
+#ifdef MOZ_APPLEMEDIA
+ if (kind == ipc::SandboxingKind::UTILITY_AUDIO_DECODING_APPLE_MEDIA) {
+ AppleDecoderModule::Init();
+ }
+#endif
+#ifdef MOZ_FFVPX
+ if (kind == ipc::SandboxingKind::GENERIC_UTILITY) {
+ FFVPXRuntimeLinker::Init();
+ }
+#endif
+#ifdef MOZ_FFMPEG
+ if (StaticPrefs::media_utility_ffmpeg_enabled() &&
+ kind == ipc::SandboxingKind::GENERIC_UTILITY) {
+ FFmpegRuntimeLinker::Init();
+ }
+#endif
+ }
+
+ static void InitContentPDMs() {
+#ifdef XP_WIN
+ if (!IsWin7AndPre2000Compatible()) {
+# ifdef MOZ_WMF
+ if (!StaticPrefs::media_rdd_process_enabled() ||
+ !StaticPrefs::media_rdd_wmf_enabled() ||
+ !StaticPrefs::media_utility_process_enabled() ||
+ !StaticPrefs::media_utility_wmf_enabled()) {
+ WMFDecoderModule::Init();
+ }
+# endif
+ }
+#endif
+#ifdef MOZ_APPLEMEDIA
+ AppleDecoderModule::Init();
+#endif
+#ifdef MOZ_OMX
+ OmxDecoderModule::Init();
+#endif
+#ifdef MOZ_FFVPX
+ FFVPXRuntimeLinker::Init();
+#endif
+#ifdef MOZ_FFMPEG
+ FFmpegRuntimeLinker::Init();
+#endif
+ RemoteDecoderManagerChild::Init();
+ }
+
+ static void InitDefaultPDMs() {
+#ifdef XP_WIN
+ if (!IsWin7AndPre2000Compatible()) {
+ WMFDecoderModule::Init();
+ }
+#endif
+#ifdef MOZ_APPLEMEDIA
+ AppleDecoderModule::Init();
+#endif
+#ifdef MOZ_OMX
+ OmxDecoderModule::Init();
+#endif
+#ifdef MOZ_FFVPX
+ FFVPXRuntimeLinker::Init();
+#endif
+#ifdef MOZ_FFMPEG
+ FFmpegRuntimeLinker::Init();
+#endif
+ }
+
+ static bool sHasInitializedPDMs;
+ static StaticMutex sMonitor MOZ_UNANNOTATED;
+};
+
+bool PDMInitializer::sHasInitializedPDMs = false;
+StaticMutex PDMInitializer::sMonitor;
+
+/* static */
+void PDMInitializer::InitPDMs() {
+ StaticMutexAutoLock mon(sMonitor);
+ MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sHasInitializedPDMs);
+ if (XRE_IsGPUProcess()) {
+ PDM_INIT_LOG("Init PDMs in GPU process");
+ InitGpuPDMs();
+ } else if (XRE_IsRDDProcess()) {
+ PDM_INIT_LOG("Init PDMs in RDD process");
+ InitRddPDMs();
+ } else if (XRE_IsUtilityProcess()) {
+ PDM_INIT_LOG("Init PDMs in Utility process");
+ InitUtilityPDMs();
+ } else if (XRE_IsContentProcess()) {
+ PDM_INIT_LOG("Init PDMs in Content process");
+ InitContentPDMs();
+ } else {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(),
+ "PDMFactory is only usable in the "
+ "Parent/GPU/RDD/Utility/Content process");
+ PDM_INIT_LOG("Init PDMs in Chrome process");
+ InitDefaultPDMs();
+ }
+ sHasInitializedPDMs = true;
+}
+
+/* static */
+bool PDMInitializer::HasInitializedPDMs() {
+ StaticMutexAutoLock mon(sMonitor);
+ return sHasInitializedPDMs;
+}
+
+class SupportChecker {
+ public:
+ enum class Reason : uint8_t {
+ kSupported,
+ kVideoFormatNotSupported,
+ kAudioFormatNotSupported,
+ kUnknown,
+ };
+
+ struct CheckResult {
+ explicit CheckResult(Reason aReason,
+ MediaResult aResult = MediaResult(NS_OK))
+ : mReason(aReason), mMediaResult(std::move(aResult)) {}
+ CheckResult(const CheckResult& aOther) = default;
+ CheckResult(CheckResult&& aOther) = default;
+ CheckResult& operator=(const CheckResult& aOther) = default;
+ CheckResult& operator=(CheckResult&& aOther) = default;
+
+ Reason mReason;
+ MediaResult mMediaResult;
+ };
+
+ template <class Func>
+ void AddToCheckList(Func&& aChecker) {
+ mCheckerList.AppendElement(std::forward<Func>(aChecker));
+ }
+
+ void AddMediaFormatChecker(const TrackInfo& aTrackConfig) {
+ if (aTrackConfig.IsVideo()) {
+ auto mimeType = aTrackConfig.GetAsVideoInfo()->mMimeType;
+ RefPtr<MediaByteBuffer> extraData =
+ aTrackConfig.GetAsVideoInfo()->mExtraData;
+ AddToCheckList([mimeType, extraData]() {
+ if (MP4Decoder::IsH264(mimeType)) {
+ SPSData spsdata;
+ // WMF H.264 Video Decoder and Apple ATDecoder
+ // do not support YUV444 format.
+ // For consistency, all decoders should be checked.
+ if (H264::DecodeSPSFromExtraData(extraData, spsdata) &&
+ (spsdata.profile_idc == 244 /* Hi444PP */ ||
+ spsdata.chroma_format_idc == PDMFactory::kYUV444)) {
+ return CheckResult(
+ SupportChecker::Reason::kVideoFormatNotSupported,
+ MediaResult(
+ NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ RESULT_DETAIL("Decoder may not have the capability "
+ "to handle the requested video format "
+ "with YUV444 chroma subsampling.")));
+ }
+ }
+ return CheckResult(SupportChecker::Reason::kSupported);
+ });
+ }
+ }
+
+ SupportChecker::CheckResult Check() {
+ for (auto& checker : mCheckerList) {
+ auto result = checker();
+ if (result.mReason != SupportChecker::Reason::kSupported) {
+ return result;
+ }
+ }
+ return CheckResult(SupportChecker::Reason::kSupported);
+ }
+
+ void Clear() { mCheckerList.Clear(); }
+
+ private:
+ nsTArray<std::function<CheckResult()>> mCheckerList;
+}; // SupportChecker
+
+PDMFactory::PDMFactory() {
+ EnsureInit();
+ CreatePDMs();
+ CreateNullPDM();
+}
+
+PDMFactory::~PDMFactory() = default;
+
+/* static */
+void PDMFactory::EnsureInit() {
+ if (PDMInitializer::HasInitializedPDMs()) {
+ return;
+ }
+ auto initalization = []() {
+ MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
+ if (!PDMInitializer::HasInitializedPDMs()) {
+ // Ensure that all system variables are initialized.
+ gfx::gfxVars::Initialize();
+ // Prime the preferences system from the main thread.
+ Unused << BrowserTabsRemoteAutostart();
+ PDMInitializer::InitPDMs();
+ }
+ };
+ // If on the main thread, then initialize PDMs. Otherwise, do a sync-dispatch
+ // to main thread.
+ if (NS_IsMainThread()) {
+ initalization();
+ return;
+ }
+ nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadSerialEventTarget();
+ nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction(
+ "PDMFactory::EnsureInit", std::move(initalization));
+ SyncRunnable::DispatchToThread(mainTarget, runnable);
+}
+
+RefPtr<PlatformDecoderModule::CreateDecoderPromise> PDMFactory::CreateDecoder(
+ const CreateDecoderParams& aParams) {
+ if (aParams.mUseNullDecoder.mUse) {
+ MOZ_ASSERT(mNullPDM);
+ return CreateDecoderWithPDM(mNullPDM, aParams);
+ }
+ bool isEncrypted = mEMEPDM && aParams.mConfig.mCrypto.IsEncrypted();
+
+ if (isEncrypted) {
+ return CreateDecoderWithPDM(mEMEPDM, aParams);
+ }
+
+ return CheckAndMaybeCreateDecoder(CreateDecoderParamsForAsync(aParams), 0);
+}
+
+RefPtr<PlatformDecoderModule::CreateDecoderPromise>
+PDMFactory::CheckAndMaybeCreateDecoder(CreateDecoderParamsForAsync&& aParams,
+ uint32_t aIndex,
+ Maybe<MediaResult> aEarlierError) {
+ uint32_t i = aIndex;
+ auto params = SupportDecoderParams(aParams);
+ for (; i < mCurrentPDMs.Length(); i++) {
+ if (mCurrentPDMs[i]->Supports(params, nullptr /* diagnostic */) ==
+ media::DecodeSupport::Unsupported) {
+ continue;
+ }
+ RefPtr<PlatformDecoderModule::CreateDecoderPromise> p =
+ CreateDecoderWithPDM(mCurrentPDMs[i], aParams)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [](RefPtr<MediaDataDecoder>&& aDecoder) {
+ return PlatformDecoderModule::CreateDecoderPromise::
+ CreateAndResolve(std::move(aDecoder), __func__);
+ },
+ [self = RefPtr{this}, i, params = std::move(aParams)](
+ const MediaResult& aError) mutable {
+ // Try the next PDM.
+ return self->CheckAndMaybeCreateDecoder(std::move(params),
+ i + 1, Some(aError));
+ });
+ return p;
+ }
+ if (aEarlierError) {
+ return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
+ std::move(*aEarlierError), __func__);
+ }
+ return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
+ MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ nsPrintfCString("Error no decoder found for %s",
+ aParams.mConfig->mMimeType.get())
+ .get()),
+ __func__);
+}
+
+RefPtr<PlatformDecoderModule::CreateDecoderPromise>
+PDMFactory::CreateDecoderWithPDM(PlatformDecoderModule* aPDM,
+ const CreateDecoderParams& aParams) {
+ MOZ_ASSERT(aPDM);
+ MediaResult result = NS_OK;
+
+ SupportChecker supportChecker;
+ const TrackInfo& config = aParams.mConfig;
+ supportChecker.AddMediaFormatChecker(config);
+
+ auto checkResult = supportChecker.Check();
+ if (checkResult.mReason != SupportChecker::Reason::kSupported) {
+ if (checkResult.mReason ==
+ SupportChecker::Reason::kVideoFormatNotSupported) {
+ result = checkResult.mMediaResult;
+ } else if (checkResult.mReason ==
+ SupportChecker::Reason::kAudioFormatNotSupported) {
+ result = checkResult.mMediaResult;
+ }
+ return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
+ result, __func__);
+ }
+
+ if (config.IsAudio()) {
+ RefPtr<PlatformDecoderModule::CreateDecoderPromise> p;
+ p = aPDM->AsyncCreateDecoder(aParams)->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [params = CreateDecoderParamsForAsync(aParams)](
+ RefPtr<MediaDataDecoder>&& aDecoder) {
+ RefPtr<MediaDataDecoder> decoder = std::move(aDecoder);
+ if (!params.mNoWrapper.mDontUseWrapper) {
+ decoder =
+ new AudioTrimmer(decoder.forget(), CreateDecoderParams(params));
+ }
+ return PlatformDecoderModule::CreateDecoderPromise::CreateAndResolve(
+ decoder, __func__);
+ },
+ [](const MediaResult& aError) {
+ return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
+ aError, __func__);
+ });
+ return p;
+ }
+
+ if (!config.IsVideo()) {
+ return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
+ MediaResult(
+ NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ RESULT_DETAIL(
+ "Decoder configuration error, expected audio or video.")),
+ __func__);
+ }
+
+ if ((MP4Decoder::IsH264(config.mMimeType) ||
+#ifdef MOZ_AV1
+ AOMDecoder::IsAV1(config.mMimeType) ||
+#endif
+ VPXDecoder::IsVPX(config.mMimeType)) &&
+ !aParams.mUseNullDecoder.mUse && !aParams.mNoWrapper.mDontUseWrapper) {
+ return MediaChangeMonitor::Create(this, aParams);
+ }
+ return aPDM->AsyncCreateDecoder(aParams);
+}
+
+DecodeSupportSet PDMFactory::SupportsMimeType(
+ const nsACString& aMimeType) const {
+ UniquePtr<TrackInfo> trackInfo = CreateTrackInfoWithMIMEType(aMimeType);
+ if (!trackInfo) {
+ return DecodeSupport::Unsupported;
+ }
+ return Supports(SupportDecoderParams(*trackInfo), nullptr);
+}
+
+DecodeSupportSet PDMFactory::Supports(
+ const SupportDecoderParams& aParams,
+ DecoderDoctorDiagnostics* aDiagnostics) const {
+ if (mEMEPDM) {
+ return mEMEPDM->Supports(aParams, aDiagnostics);
+ }
+
+ RefPtr<PlatformDecoderModule> current =
+ GetDecoderModule(aParams, aDiagnostics);
+
+ if (!current) {
+ return DecodeSupport::Unsupported;
+ }
+
+ // We have a PDM - check for + return SW/HW support info
+ return current->Supports(aParams, aDiagnostics);
+}
+
+void PDMFactory::CreatePDMs() {
+ if (StaticPrefs::media_use_blank_decoder()) {
+ CreateAndStartupPDM<BlankDecoderModule>();
+ // The Blank PDM SupportsMimeType reports true for all codecs; the creation
+ // of its decoder is infallible. As such it will be used for all media, we
+ // can stop creating more PDM from this point.
+ return;
+ }
+
+ if (XRE_IsGPUProcess()) {
+ CreateGpuPDMs();
+ } else if (XRE_IsRDDProcess()) {
+ CreateRddPDMs();
+ } else if (XRE_IsUtilityProcess()) {
+ CreateUtilityPDMs();
+ } else if (XRE_IsContentProcess()) {
+ CreateContentPDMs();
+ } else {
+ MOZ_DIAGNOSTIC_ASSERT(
+ XRE_IsParentProcess(),
+ "PDMFactory is only usable in the Parent/GPU/RDD/Content process");
+ CreateDefaultPDMs();
+ }
+}
+
+void PDMFactory::CreateGpuPDMs() {
+#ifdef XP_WIN
+ if (StaticPrefs::media_wmf_enabled() && !IsWin7AndPre2000Compatible()) {
+ CreateAndStartupPDM<WMFDecoderModule>();
+ }
+#endif
+}
+
+#if defined(MOZ_FFMPEG)
+static DecoderDoctorDiagnostics::Flags GetFailureFlagBasedOnFFmpegStatus(
+ const FFmpegRuntimeLinker::LinkStatus& aStatus) {
+ switch (aStatus) {
+ case FFmpegRuntimeLinker::LinkStatus_INVALID_FFMPEG_CANDIDATE:
+ case FFmpegRuntimeLinker::LinkStatus_UNUSABLE_LIBAV57:
+ case FFmpegRuntimeLinker::LinkStatus_INVALID_LIBAV_CANDIDATE:
+ case FFmpegRuntimeLinker::LinkStatus_OBSOLETE_FFMPEG:
+ case FFmpegRuntimeLinker::LinkStatus_OBSOLETE_LIBAV:
+ case FFmpegRuntimeLinker::LinkStatus_INVALID_CANDIDATE:
+ return DecoderDoctorDiagnostics::Flags::LibAVCodecUnsupported;
+ default:
+ MOZ_DIAGNOSTIC_ASSERT(
+ aStatus == FFmpegRuntimeLinker::LinkStatus_NOT_FOUND,
+ "Only call this method when linker fails.");
+ return DecoderDoctorDiagnostics::Flags::FFmpegNotFound;
+ }
+}
+#endif
+
+void PDMFactory::CreateRddPDMs() {
+#ifdef XP_WIN
+ if (StaticPrefs::media_wmf_enabled() &&
+ StaticPrefs::media_rdd_wmf_enabled()) {
+ CreateAndStartupPDM<WMFDecoderModule>();
+ }
+#endif
+#ifdef MOZ_APPLEMEDIA
+ if (StaticPrefs::media_rdd_applemedia_enabled()) {
+ CreateAndStartupPDM<AppleDecoderModule>();
+ }
+#endif
+#ifdef MOZ_FFVPX
+ if (StaticPrefs::media_ffvpx_enabled() &&
+ StaticPrefs::media_rdd_ffvpx_enabled()) {
+ CreateAndStartupPDM<FFVPXRuntimeLinker>();
+ }
+#endif
+#ifdef MOZ_FFMPEG
+ if (StaticPrefs::media_ffmpeg_enabled() &&
+ StaticPrefs::media_rdd_ffmpeg_enabled() &&
+ !CreateAndStartupPDM<FFmpegRuntimeLinker>()) {
+ mFailureFlags += GetFailureFlagBasedOnFFmpegStatus(
+ FFmpegRuntimeLinker::LinkStatusCode());
+ }
+#endif
+ CreateAndStartupPDM<AgnosticDecoderModule>();
+}
+
+void PDMFactory::CreateUtilityPDMs() {
+ const ipc::SandboxingKind aKind = GetCurrentSandboxingKind();
+#ifdef XP_WIN
+ if (StaticPrefs::media_wmf_enabled() &&
+ StaticPrefs::media_utility_wmf_enabled() &&
+ aKind == ipc::SandboxingKind::UTILITY_AUDIO_DECODING_WMF) {
+ CreateAndStartupPDM<WMFDecoderModule>();
+ }
+#endif
+#ifdef MOZ_APPLEMEDIA
+ if (StaticPrefs::media_utility_applemedia_enabled() &&
+ aKind == ipc::SandboxingKind::UTILITY_AUDIO_DECODING_APPLE_MEDIA) {
+ CreateAndStartupPDM<AppleDecoderModule>();
+ }
+#endif
+ if (aKind == ipc::SandboxingKind::GENERIC_UTILITY) {
+#ifdef MOZ_FFVPX
+ if (StaticPrefs::media_ffvpx_enabled() &&
+ StaticPrefs::media_utility_ffvpx_enabled()) {
+ CreateAndStartupPDM<FFVPXRuntimeLinker>();
+ }
+#endif
+#ifdef MOZ_FFMPEG
+ if (StaticPrefs::media_ffmpeg_enabled() &&
+ StaticPrefs::media_utility_ffmpeg_enabled() &&
+ !CreateAndStartupPDM<FFmpegRuntimeLinker>()) {
+ mFailureFlags += GetFailureFlagBasedOnFFmpegStatus(
+ FFmpegRuntimeLinker::LinkStatusCode());
+ }
+#endif
+#ifdef MOZ_WIDGET_ANDROID
+ if (StaticPrefs::media_utility_android_media_codec_enabled()) {
+ StartupPDM(AndroidDecoderModule::Create(),
+ StaticPrefs::media_android_media_codec_preferred());
+ }
+#endif
+ CreateAndStartupPDM<AgnosticDecoderModule>();
+ }
+#ifdef MOZ_WMF_MEDIA_ENGINE
+ if (aKind == ipc::SandboxingKind::MF_MEDIA_ENGINE_CDM) {
+ if (IsWin10OrLater() && StaticPrefs::media_wmf_media_engine_enabled()) {
+ CreateAndStartupPDM<MFMediaEngineDecoderModule>();
+ }
+ }
+#endif
+}
+
+void PDMFactory::CreateContentPDMs() {
+ if (StaticPrefs::media_gpu_process_decoder()) {
+ CreateAndStartupPDM<RemoteDecoderModule>(RemoteDecodeIn::GpuProcess);
+ }
+
+ if (StaticPrefs::media_rdd_process_enabled()) {
+ CreateAndStartupPDM<RemoteDecoderModule>(RemoteDecodeIn::RddProcess);
+ }
+
+ if (StaticPrefs::media_utility_process_enabled()) {
+#ifdef MOZ_APPLEMEDIA
+ CreateAndStartupPDM<RemoteDecoderModule>(
+ RemoteDecodeIn::UtilityProcess_AppleMedia);
+#endif
+#ifdef XP_WIN
+ CreateAndStartupPDM<RemoteDecoderModule>(
+ RemoteDecodeIn::UtilityProcess_WMF);
+#endif
+ // WMF and AppleMedia should be created before Generic because the order
+ // affects what decoder module would be chose first.
+ CreateAndStartupPDM<RemoteDecoderModule>(
+ RemoteDecodeIn::UtilityProcess_Generic);
+ }
+#ifdef MOZ_WMF_MEDIA_ENGINE
+ if (StaticPrefs::media_wmf_media_engine_enabled()) {
+ CreateAndStartupPDM<RemoteDecoderModule>(
+ RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM);
+ }
+#endif
+
+#ifdef XP_WIN
+ if (StaticPrefs::media_wmf_enabled() && !IsWin7AndPre2000Compatible()) {
+# ifdef MOZ_WMF
+ if (!StaticPrefs::media_rdd_process_enabled() ||
+ !StaticPrefs::media_rdd_wmf_enabled()) {
+ if (!CreateAndStartupPDM<WMFDecoderModule>()) {
+ mFailureFlags += DecoderDoctorDiagnostics::Flags::WMFFailedToLoad;
+ }
+ }
+# endif
+ } else if (StaticPrefs::media_decoder_doctor_wmf_disabled_is_failure()) {
+ mFailureFlags += DecoderDoctorDiagnostics::Flags::WMFFailedToLoad;
+ }
+#endif
+
+#ifdef MOZ_APPLEMEDIA
+ CreateAndStartupPDM<AppleDecoderModule>();
+#endif
+#ifdef MOZ_OMX
+ if (StaticPrefs::media_omx_enabled()) {
+ CreateAndStartupPDM<OmxDecoderModule>();
+ }
+#endif
+#ifdef MOZ_FFVPX
+ if (StaticPrefs::media_ffvpx_enabled()) {
+ CreateAndStartupPDM<FFVPXRuntimeLinker>();
+ }
+#endif
+#ifdef MOZ_FFMPEG
+ if (StaticPrefs::media_ffmpeg_enabled() &&
+ !CreateAndStartupPDM<FFmpegRuntimeLinker>()) {
+ mFailureFlags += GetFailureFlagBasedOnFFmpegStatus(
+ FFmpegRuntimeLinker::LinkStatusCode());
+ }
+#endif
+#ifdef MOZ_WIDGET_ANDROID
+ if (StaticPrefs::media_android_media_codec_enabled()) {
+ StartupPDM(AndroidDecoderModule::Create(),
+ StaticPrefs::media_android_media_codec_preferred());
+ }
+#endif
+
+ CreateAndStartupPDM<AgnosticDecoderModule>();
+
+ if (StaticPrefs::media_gmp_decoder_enabled() &&
+ !StartupPDM(GMPDecoderModule::Create(),
+ StaticPrefs::media_gmp_decoder_preferred())) {
+ mFailureFlags += DecoderDoctorDiagnostics::Flags::GMPPDMFailedToStartup;
+ }
+}
+
+void PDMFactory::CreateDefaultPDMs() {
+#ifdef XP_WIN
+ if (StaticPrefs::media_wmf_enabled() && !IsWin7AndPre2000Compatible()) {
+ if (!CreateAndStartupPDM<WMFDecoderModule>()) {
+ mFailureFlags += DecoderDoctorDiagnostics::Flags::WMFFailedToLoad;
+ }
+ } else if (StaticPrefs::media_decoder_doctor_wmf_disabled_is_failure()) {
+ mFailureFlags += DecoderDoctorDiagnostics::Flags::WMFFailedToLoad;
+ }
+#endif
+
+#ifdef MOZ_APPLEMEDIA
+ CreateAndStartupPDM<AppleDecoderModule>();
+#endif
+#ifdef MOZ_OMX
+ if (StaticPrefs::media_omx_enabled()) {
+ CreateAndStartupPDM<OmxDecoderModule>();
+ }
+#endif
+#ifdef MOZ_FFVPX
+ if (StaticPrefs::media_ffvpx_enabled()) {
+ CreateAndStartupPDM<FFVPXRuntimeLinker>();
+ }
+#endif
+#ifdef MOZ_FFMPEG
+ if (StaticPrefs::media_ffmpeg_enabled() &&
+ !CreateAndStartupPDM<FFmpegRuntimeLinker>()) {
+ mFailureFlags += GetFailureFlagBasedOnFFmpegStatus(
+ FFmpegRuntimeLinker::LinkStatusCode());
+ }
+#endif
+#ifdef MOZ_WIDGET_ANDROID
+ if (StaticPrefs::media_android_media_codec_enabled()) {
+ StartupPDM(AndroidDecoderModule::Create(),
+ StaticPrefs::media_android_media_codec_preferred());
+ }
+#endif
+
+ CreateAndStartupPDM<AgnosticDecoderModule>();
+
+ if (StaticPrefs::media_gmp_decoder_enabled() &&
+ !StartupPDM(GMPDecoderModule::Create(),
+ StaticPrefs::media_gmp_decoder_preferred())) {
+ mFailureFlags += DecoderDoctorDiagnostics::Flags::GMPPDMFailedToStartup;
+ }
+}
+
+void PDMFactory::CreateNullPDM() {
+ mNullPDM = CreateNullDecoderModule();
+ MOZ_ASSERT(mNullPDM && NS_SUCCEEDED(mNullPDM->Startup()));
+}
+
+bool PDMFactory::StartupPDM(already_AddRefed<PlatformDecoderModule> aPDM,
+ bool aInsertAtBeginning) {
+ RefPtr<PlatformDecoderModule> pdm = aPDM;
+ if (pdm && NS_SUCCEEDED(pdm->Startup())) {
+ if (aInsertAtBeginning) {
+ mCurrentPDMs.InsertElementAt(0, pdm);
+ } else {
+ mCurrentPDMs.AppendElement(pdm);
+ }
+ return true;
+ }
+ return false;
+}
+
+already_AddRefed<PlatformDecoderModule> PDMFactory::GetDecoderModule(
+ const SupportDecoderParams& aParams,
+ DecoderDoctorDiagnostics* aDiagnostics) const {
+ if (aDiagnostics) {
+ // If libraries failed to load, the following loop over mCurrentPDMs
+ // will not even try to use them. So we record failures now.
+ aDiagnostics->SetFailureFlags(mFailureFlags);
+ }
+
+ RefPtr<PlatformDecoderModule> pdm;
+ for (const auto& current : mCurrentPDMs) {
+ if (current->Supports(aParams, aDiagnostics) !=
+ media::DecodeSupport::Unsupported) {
+ pdm = current;
+ break;
+ }
+ }
+ return pdm.forget();
+}
+
+void PDMFactory::SetCDMProxy(CDMProxy* aProxy) {
+ MOZ_ASSERT(aProxy);
+
+#ifdef MOZ_WIDGET_ANDROID
+ if (IsWidevineKeySystem(aProxy->KeySystem())) {
+ mEMEPDM = AndroidDecoderModule::Create(aProxy);
+ return;
+ }
+#endif
+#ifdef MOZ_WMF_CDM
+ if (IsPlayReadyKeySystemAndSupported(aProxy->KeySystem())) {
+ mEMEPDM = RemoteDecoderModule::Create(
+ RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM);
+ return;
+ }
+#endif
+ auto m = MakeRefPtr<PDMFactory>();
+ mEMEPDM = MakeRefPtr<EMEDecoderModule>(aProxy, m);
+}
+
+/* static */
+media::MediaCodecsSupported PDMFactory::Supported(bool aForceRefresh) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ static auto calculate = []() {
+ auto pdm = MakeRefPtr<PDMFactory>();
+ MediaCodecsSupported supported;
+ // H264 and AAC depends on external framework that must be dynamically
+ // loaded.
+ // We currently only ship a single PDM per platform able to decode AAC or
+ // H264. As such we can assert that being able to create a H264 or AAC
+ // decoder indicates that with WMF on Windows or FFmpeg on Unixes is
+ // available.
+ // This logic will have to be revisited if a PDM supporting either codec
+ // will be added in addition to the WMF and FFmpeg PDM (such as OpenH264)
+ for (const auto& cd : MCSInfo::GetAllCodecDefinitions()) {
+ supported += MCSInfo::GetDecodeMediaCodecsSupported(
+ cd.codec, pdm->SupportsMimeType(nsCString(cd.mimeTypeString)));
+ }
+ return supported;
+ };
+
+ static MediaCodecsSupported supported = calculate();
+ if (aForceRefresh) {
+ supported = calculate();
+ }
+
+ return supported;
+}
+
+/* static */
+DecodeSupportSet PDMFactory::SupportsMimeType(
+ const nsACString& aMimeType, const MediaCodecsSupported& aSupported,
+ RemoteDecodeIn aLocation) {
+ const TrackSupportSet supports =
+ RemoteDecoderManagerChild::GetTrackSupport(aLocation);
+
+ if (supports.contains(TrackSupport::Video)) {
+ if (MP4Decoder::IsH264(aMimeType)) {
+ return MCSInfo::GetDecodeSupportSet(MediaCodec::H264, aSupported);
+ }
+ if (VPXDecoder::IsVP9(aMimeType)) {
+ return MCSInfo::GetDecodeSupportSet(MediaCodec::VP9, aSupported);
+ }
+ if (VPXDecoder::IsVP8(aMimeType)) {
+ return MCSInfo::GetDecodeSupportSet(MediaCodec::VP8, aSupported);
+ }
+#ifdef MOZ_AV1
+ if (AOMDecoder::IsAV1(aMimeType)) {
+ return MCSInfo::GetDecodeSupportSet(MediaCodec::AV1, aSupported);
+ }
+#endif
+ if (TheoraDecoder::IsTheora(aMimeType)) {
+ return MCSInfo::GetDecodeSupportSet(MediaCodec::Theora, aSupported);
+ }
+ }
+
+ if (supports.contains(TrackSupport::Audio)) {
+ if (MP4Decoder::IsAAC(aMimeType)) {
+ return MCSInfo::GetDecodeSupportSet(MediaCodec::AAC, aSupported);
+ }
+ if (aMimeType.EqualsLiteral("audio/mpeg")) {
+ return MCSInfo::GetDecodeSupportSet(MediaCodec::MP3, aSupported);
+ }
+ if (OpusDataDecoder::IsOpus(aMimeType)) {
+ return MCSInfo::GetDecodeSupportSet(MediaCodec::Opus, aSupported);
+ }
+ if (VorbisDataDecoder::IsVorbis(aMimeType)) {
+ return MCSInfo::GetDecodeSupportSet(MediaCodec::Vorbis, aSupported);
+ }
+ if (aMimeType.EqualsLiteral("audio/flac")) {
+ return MCSInfo::GetDecodeSupportSet(MediaCodec::FLAC, aSupported);
+ }
+ if (WaveDataDecoder::IsWave(aMimeType)) {
+ return MCSInfo::GetDecodeSupportSet(MediaCodec::Wave, aSupported);
+ }
+ }
+ return DecodeSupport::Unsupported;
+}
+
+/* static */
+bool PDMFactory::AllDecodersAreRemote() {
+ return StaticPrefs::media_rdd_process_enabled() &&
+#if defined(MOZ_FFVPX)
+ StaticPrefs::media_rdd_ffvpx_enabled() &&
+#endif
+ StaticPrefs::media_rdd_opus_enabled() &&
+ StaticPrefs::media_rdd_theora_enabled() &&
+ StaticPrefs::media_rdd_vorbis_enabled() &&
+ StaticPrefs::media_rdd_vpx_enabled() &&
+#if defined(MOZ_WMF)
+ StaticPrefs::media_rdd_wmf_enabled() &&
+#endif
+ StaticPrefs::media_rdd_wav_enabled();
+}
+
+#undef PDM_INIT_LOG
+} // namespace mozilla