/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "RemoteDecoderManagerChild.h" #include "PDMFactory.h" #include "RemoteAudioDecoder.h" #include "RemoteMediaDataDecoder.h" #include "RemoteVideoDecoder.h" #include "VideoUtils.h" #include "mozilla/DataMutex.h" #include "mozilla/RemoteDecodeUtils.h" #include "mozilla/SyncRunnable.h" #include "mozilla/dom/ContentChild.h" // for launching RDD w/ ContentChild #include "mozilla/gfx/2D.h" #include "mozilla/gfx/DataSurfaceHelpers.h" #include "mozilla/ipc/BackgroundChild.h" #include "mozilla/ipc/PBackgroundChild.h" #include "mozilla/ipc/Endpoint.h" #include "mozilla/layers/ISurfaceAllocator.h" #include "mozilla/ipc/UtilityAudioDecoderChild.h" #include "nsContentUtils.h" #include "nsIObserver.h" #include "mozilla/StaticPrefs_media.h" #ifdef MOZ_WMF_MEDIA_ENGINE # include "MFMediaEngineChild.h" #endif #ifdef MOZ_WMF_CDM # include "MFCDMChild.h" #endif namespace mozilla { #define LOG(msg, ...) \ MOZ_LOG(gRemoteDecodeLog, LogLevel::Debug, (msg, ##__VA_ARGS__)) using namespace layers; using namespace gfx; // Used so that we only ever attempt to check if the RDD/GPU/Utility processes // should be launched serially. Protects sLaunchPromise StaticMutex sLaunchMutex; static EnumeratedArray> sLaunchPromises MOZ_GUARDED_BY(sLaunchMutex); // Only modified on the main-thread, read on any thread. While it could be read // on the main thread directly, for clarity we force access via the DataMutex // wrapper. static StaticDataMutex> sRemoteDecoderManagerChildThread("sRemoteDecoderManagerChildThread"); // Only accessed from sRemoteDecoderManagerChildThread static EnumeratedArray> sRemoteDecoderManagerChildForProcesses; static UniquePtr>> sRecreateTasks; // Used for protecting codec support information collected from different remote // processes. StaticMutex sProcessSupportedMutex; static EnumeratedArray> sProcessSupported MOZ_GUARDED_BY(sProcessSupportedMutex); class ShutdownObserver final : public nsIObserver { public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER protected: ~ShutdownObserver() = default; }; NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver); NS_IMETHODIMP ShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)); RemoteDecoderManagerChild::Shutdown(); return NS_OK; } StaticRefPtr sObserver; /* static */ void RemoteDecoderManagerChild::Init() { MOZ_ASSERT(NS_IsMainThread()); LOG("RemoteDecoderManagerChild Init"); auto remoteDecoderManagerThread = sRemoteDecoderManagerChildThread.Lock(); if (!*remoteDecoderManagerThread) { LOG("RemoteDecoderManagerChild's thread is created"); // We can't use a MediaThreadType::SUPERVISOR as the RemoteDecoderModule // runs on it and dispatch synchronous tasks to the manager thread, should // more than 4 concurrent videos being instantiated at the same time, we // could end up in a deadlock. RefPtr childThread; nsresult rv = NS_NewNamedThread( "RemVidChild", getter_AddRefs(childThread), NS_NewRunnableFunction( "RemoteDecoderManagerChild::InitPBackground", []() { ipc::PBackgroundChild* bgActor = ipc::BackgroundChild::GetOrCreateForCurrentThread(); NS_WARNING_ASSERTION(bgActor, "Failed to start Background channel"); Unused << bgActor; })); NS_ENSURE_SUCCESS_VOID(rv); *remoteDecoderManagerThread = childThread; sRecreateTasks = MakeUnique>>(); sObserver = new ShutdownObserver(); nsContentUtils::RegisterShutdownObserver(sObserver); } } /* static */ void RemoteDecoderManagerChild::InitForGPUProcess( Endpoint&& aVideoManager) { MOZ_ASSERT(NS_IsMainThread()); Init(); auto remoteDecoderManagerThread = sRemoteDecoderManagerChildThread.Lock(); MOZ_ALWAYS_SUCCEEDS( (*remoteDecoderManagerThread) ->Dispatch(NewRunnableFunction( "InitForContentRunnable", &OpenRemoteDecoderManagerChildForProcess, std::move(aVideoManager), RemoteDecodeIn::GpuProcess))); } /* static */ void RemoteDecoderManagerChild::Shutdown() { MOZ_ASSERT(NS_IsMainThread()); LOG("RemoteDecoderManagerChild Shutdown"); if (sObserver) { nsContentUtils::UnregisterShutdownObserver(sObserver); sObserver = nullptr; } nsCOMPtr childThread; { auto remoteDecoderManagerThread = sRemoteDecoderManagerChildThread.Lock(); childThread = remoteDecoderManagerThread->forget(); LOG("RemoteDecoderManagerChild's thread is released"); } if (childThread) { MOZ_ALWAYS_SUCCEEDS(childThread->Dispatch(NS_NewRunnableFunction( "dom::RemoteDecoderManagerChild::Shutdown", []() { for (auto& p : sRemoteDecoderManagerChildForProcesses) { if (p && p->CanSend()) { p->Close(); } p = nullptr; } { StaticMutexAutoLock lock(sLaunchMutex); for (auto& p : sLaunchPromises) { p = nullptr; } } ipc::BackgroundChild::CloseForCurrentThread(); }))); childThread->Shutdown(); sRecreateTasks = nullptr; } } void RemoteDecoderManagerChild::RunWhenGPUProcessRecreated( already_AddRefed aTask) { nsCOMPtr managerThread = GetManagerThread(); if (!managerThread) { // We've been shutdown, bail. return; } MOZ_ASSERT(managerThread->IsOnCurrentThread()); // If we've already been recreated, then run the task immediately. auto* manager = GetSingleton(RemoteDecodeIn::GpuProcess); if (manager && manager != this && manager->CanSend()) { RefPtr task = aTask; task->Run(); } else { sRecreateTasks->AppendElement(aTask); } } /* static */ RemoteDecoderManagerChild* RemoteDecoderManagerChild::GetSingleton( RemoteDecodeIn aLocation) { nsCOMPtr managerThread = GetManagerThread(); if (!managerThread) { // We've been shutdown, bail. return nullptr; } MOZ_ASSERT(managerThread->IsOnCurrentThread()); switch (aLocation) { case RemoteDecodeIn::GpuProcess: case RemoteDecodeIn::RddProcess: case RemoteDecodeIn::UtilityProcess_Generic: case RemoteDecodeIn::UtilityProcess_AppleMedia: case RemoteDecodeIn::UtilityProcess_WMF: case RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM: return sRemoteDecoderManagerChildForProcesses[aLocation]; default: MOZ_CRASH("Unexpected RemoteDecode variant"); return nullptr; } } /* static */ nsISerialEventTarget* RemoteDecoderManagerChild::GetManagerThread() { auto remoteDecoderManagerThread = sRemoteDecoderManagerChildThread.Lock(); return *remoteDecoderManagerThread; } /* static */ bool RemoteDecoderManagerChild::Supports( RemoteDecodeIn aLocation, const SupportDecoderParams& aParams, DecoderDoctorDiagnostics* aDiagnostics) { Maybe supported; switch (aLocation) { case RemoteDecodeIn::GpuProcess: case RemoteDecodeIn::RddProcess: case RemoteDecodeIn::UtilityProcess_AppleMedia: case RemoteDecodeIn::UtilityProcess_Generic: case RemoteDecodeIn::UtilityProcess_WMF: case RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM: { StaticMutexAutoLock lock(sProcessSupportedMutex); supported = sProcessSupported[aLocation]; break; } default: return false; } if (!supported) { // We haven't received the correct information yet from either the GPU or // the RDD process nor the Utility process. if (aLocation == RemoteDecodeIn::UtilityProcess_Generic || aLocation == RemoteDecodeIn::UtilityProcess_AppleMedia || aLocation == RemoteDecodeIn::UtilityProcess_WMF || aLocation == RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM) { LaunchUtilityProcessIfNeeded(aLocation); } if (aLocation == RemoteDecodeIn::RddProcess) { // Ensure the RDD process got started. // TODO: This can be removed once bug 1684991 is fixed. LaunchRDDProcessIfNeeded(); } // Assume the format is supported to prevent false negative, if the remote // process supports that specific track type. const bool isVideo = aParams.mConfig.IsVideo(); const bool isAudio = aParams.mConfig.IsAudio(); const auto trackSupport = GetTrackSupport(aLocation); if (isVideo) { return trackSupport.contains(TrackSupport::Video); } if (isAudio) { return trackSupport.contains(TrackSupport::Audio); } MOZ_ASSERT_UNREACHABLE("Not audio and video?!"); return false; } // We can ignore the SupportDecoderParams argument for now as creation of the // decoder will actually fail later and fallback PDMs will be tested on later. return PDMFactory::SupportsMimeType(aParams.MimeType(), *supported, aLocation) != media::DecodeSupport::Unsupported; } /* static */ RefPtr RemoteDecoderManagerChild::CreateAudioDecoder( const CreateDecoderParams& aParams, RemoteDecodeIn aLocation) { nsCOMPtr managerThread = GetManagerThread(); if (!managerThread) { // We got shutdown. return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject( NS_ERROR_DOM_MEDIA_CANCELED, __func__); } if (!GetTrackSupport(aLocation).contains(TrackSupport::Audio)) { return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject( MediaResult(NS_ERROR_DOM_MEDIA_CANCELED, nsPrintfCString("%s doesn't support audio decoding", RemoteDecodeInToStr(aLocation)) .get()), __func__); } RefPtr launchPromise; if (StaticPrefs::media_utility_process_enabled() && (aLocation == RemoteDecodeIn::UtilityProcess_Generic || aLocation == RemoteDecodeIn::UtilityProcess_AppleMedia || aLocation == RemoteDecodeIn::UtilityProcess_WMF)) { launchPromise = LaunchUtilityProcessIfNeeded(aLocation); } else if (aLocation == RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM) { launchPromise = LaunchUtilityProcessIfNeeded(aLocation); } else { launchPromise = LaunchRDDProcessIfNeeded(); } LOG("Create audio decoder in %s", RemoteDecodeInToStr(aLocation)); return launchPromise->Then( managerThread, __func__, [params = CreateDecoderParamsForAsync(aParams), aLocation](bool) { auto child = MakeRefPtr(aLocation); MediaResult result = child->InitIPDL( params.AudioConfig(), params.mOptions, params.mMediaEngineId); if (NS_FAILED(result)) { return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject( result, __func__); } return Construct(std::move(child), aLocation); }, [aLocation](nsresult aResult) { return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject( MediaResult(aResult, aLocation == RemoteDecodeIn::GpuProcess ? "Couldn't start GPU process" : (aLocation == RemoteDecodeIn::RddProcess ? "Couldn't start RDD process" : "Couldn't start Utility process")), __func__); }); } /* static */ RefPtr RemoteDecoderManagerChild::CreateVideoDecoder( const CreateDecoderParams& aParams, RemoteDecodeIn aLocation) { nsCOMPtr managerThread = GetManagerThread(); if (!managerThread) { // We got shutdown. return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject( NS_ERROR_DOM_MEDIA_CANCELED, __func__); } if (!aParams.mKnowsCompositor && aLocation == RemoteDecodeIn::GpuProcess) { // We don't have an image bridge; don't attempt to decode in the GPU // process. return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject( NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR, __func__); } if (!GetTrackSupport(aLocation).contains(TrackSupport::Video)) { return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject( MediaResult(NS_ERROR_DOM_MEDIA_CANCELED, nsPrintfCString("%s doesn't support video decoding", RemoteDecodeInToStr(aLocation)) .get()), __func__); } MOZ_ASSERT(aLocation != RemoteDecodeIn::Unspecified); RefPtr p; if (aLocation == RemoteDecodeIn::GpuProcess) { p = GenericNonExclusivePromise::CreateAndResolve(true, __func__); } else if (aLocation == RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM) { p = LaunchUtilityProcessIfNeeded(aLocation); } else { p = LaunchRDDProcessIfNeeded(); } LOG("Create video decoder in %s", RemoteDecodeInToStr(aLocation)); return p->Then( managerThread, __func__, [aLocation, params = CreateDecoderParamsForAsync(aParams)](bool) { auto child = MakeRefPtr(aLocation); MediaResult result = child->InitIPDL( params.VideoConfig(), params.mRate.mValue, params.mOptions, params.mKnowsCompositor ? Some(params.mKnowsCompositor->GetTextureFactoryIdentifier()) : Nothing(), params.mMediaEngineId, params.mTrackingId); if (NS_FAILED(result)) { return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject( result, __func__); } return Construct(std::move(child), aLocation); }, [](nsresult aResult) { return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject( MediaResult(aResult, "Couldn't start RDD process"), __func__); }); } /* static */ RefPtr RemoteDecoderManagerChild::Construct(RefPtr&& aChild, RemoteDecodeIn aLocation) { nsCOMPtr managerThread = GetManagerThread(); if (!managerThread) { // We got shutdown. return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject( NS_ERROR_DOM_MEDIA_CANCELED, __func__); } MOZ_ASSERT(managerThread->IsOnCurrentThread()); RefPtr p = aChild->SendConstruct()->Then( managerThread, __func__, [child = std::move(aChild)](MediaResult aResult) { if (NS_FAILED(aResult)) { // We will never get to use this remote decoder, tear it down. child->DestroyIPDL(); return PlatformDecoderModule::CreateDecoderPromise:: CreateAndReject(aResult, __func__); } return PlatformDecoderModule::CreateDecoderPromise:: CreateAndResolve(MakeRefPtr(child), __func__); }, [aLocation](const mozilla::ipc::ResponseRejectReason& aReason) { // The parent has died. nsresult err = NS_ERROR_DOM_MEDIA_REMOTE_DECODER_CRASHED_UTILITY_ERR; if (aLocation == RemoteDecodeIn::GpuProcess || aLocation == RemoteDecodeIn::RddProcess) { err = NS_ERROR_DOM_MEDIA_REMOTE_DECODER_CRASHED_RDD_OR_GPU_ERR; } else if (aLocation == RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM) { err = NS_ERROR_DOM_MEDIA_REMOTE_DECODER_CRASHED_MF_CDM_ERR; } return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject( err, __func__); }); return p; } /* static */ RefPtr RemoteDecoderManagerChild::LaunchRDDProcessIfNeeded() { MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess(), "Only supported from a content process."); nsCOMPtr managerThread = GetManagerThread(); if (!managerThread) { // We got shutdown. return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } StaticMutexAutoLock lock(sLaunchMutex); auto& rddLaunchPromise = sLaunchPromises[RemoteDecodeIn::RddProcess]; if (rddLaunchPromise) { return rddLaunchPromise; } // We have a couple possible states here. We are in a content process // and: // 1) the RDD process has never been launched. RDD should be launched // and the IPC connections setup. // 2) the RDD process has been launched, but this particular content // process has not setup (or has lost) its IPC connection. // In the code below, we assume we need to launch the RDD process and // setup the IPC connections. However, if the manager thread for // RemoteDecoderManagerChild is available we do a quick check to see // if we can send (meaning the IPC channel is open). If we can send, // then no work is necessary. If we can't send, then we call // LaunchRDDProcess which will launch RDD if necessary, and setup the // IPC connections between *this* content process and the RDD process. RefPtr p = InvokeAsync( managerThread, __func__, []() -> RefPtr { auto* rps = GetSingleton(RemoteDecodeIn::RddProcess); if (rps && rps->CanSend()) { return GenericNonExclusivePromise::CreateAndResolve(true, __func__); } nsCOMPtr managerThread = GetManagerThread(); ipc::PBackgroundChild* bgActor = ipc::BackgroundChild::GetForCurrentThread(); if (!managerThread || NS_WARN_IF(!bgActor)) { return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } return bgActor->SendEnsureRDDProcessAndCreateBridge()->Then( managerThread, __func__, [](ipc::PBackgroundChild::EnsureRDDProcessAndCreateBridgePromise:: ResolveOrRejectValue&& aResult) { nsCOMPtr managerThread = GetManagerThread(); if (!managerThread || aResult.IsReject()) { // The parent process died or we got shutdown return GenericNonExclusivePromise::CreateAndReject( NS_ERROR_FAILURE, __func__); } nsresult rv = std::get<0>(aResult.ResolveValue()); if (NS_FAILED(rv)) { return GenericNonExclusivePromise::CreateAndReject(rv, __func__); } OpenRemoteDecoderManagerChildForProcess( std::get<1>(std::move(aResult.ResolveValue())), RemoteDecodeIn::RddProcess); return GenericNonExclusivePromise::CreateAndResolve(true, __func__); }); }); // This should not be dispatched to a threadpool thread, so use managerThread p = p->Then( managerThread, __func__, [](const GenericNonExclusivePromise::ResolveOrRejectValue& aResult) { StaticMutexAutoLock lock(sLaunchMutex); sLaunchPromises[RemoteDecodeIn::RddProcess] = nullptr; return GenericNonExclusivePromise::CreateAndResolveOrReject(aResult, __func__); }); rddLaunchPromise = p; return rddLaunchPromise; } /* static */ RefPtr RemoteDecoderManagerChild::LaunchUtilityProcessIfNeeded( RemoteDecodeIn aLocation) { MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess(), "Only supported from a content process."); nsCOMPtr managerThread = GetManagerThread(); if (!managerThread) { // We got shutdown. return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } MOZ_ASSERT(aLocation == RemoteDecodeIn::UtilityProcess_Generic || aLocation == RemoteDecodeIn::UtilityProcess_AppleMedia || aLocation == RemoteDecodeIn::UtilityProcess_WMF || aLocation == RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM); StaticMutexAutoLock lock(sLaunchMutex); auto& utilityLaunchPromise = sLaunchPromises[aLocation]; if (utilityLaunchPromise) { return utilityLaunchPromise; } // We have a couple possible states here. We are in a content process // and: // 1) the Utility process has never been launched. Utility should be launched // and the IPC connections setup. // 2) the Utility process has been launched, but this particular content // process has not setup (or has lost) its IPC connection. // In the code below, we assume we need to launch the Utility process and // setup the IPC connections. However, if the manager thread for // RemoteDecoderManagerChild is available we do a quick check to see // if we can send (meaning the IPC channel is open). If we can send, // then no work is necessary. If we can't send, then we call // LaunchUtilityProcess which will launch Utility if necessary, and setup the // IPC connections between *this* content process and the Utility process. RefPtr p = InvokeAsync( managerThread, __func__, [aLocation]() -> RefPtr { auto* rps = GetSingleton(aLocation); if (rps && rps->CanSend()) { return GenericNonExclusivePromise::CreateAndResolve(true, __func__); } nsCOMPtr managerThread = GetManagerThread(); ipc::PBackgroundChild* bgActor = ipc::BackgroundChild::GetForCurrentThread(); if (!managerThread || NS_WARN_IF(!bgActor)) { return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } return bgActor->SendEnsureUtilityProcessAndCreateBridge(aLocation) ->Then(managerThread, __func__, [aLocation](ipc::PBackgroundChild:: EnsureUtilityProcessAndCreateBridgePromise:: ResolveOrRejectValue&& aResult) -> RefPtr { nsCOMPtr managerThread = GetManagerThread(); if (!managerThread || aResult.IsReject()) { // The parent process died or we got shutdown return GenericNonExclusivePromise::CreateAndReject( NS_ERROR_FAILURE, __func__); } nsresult rv = std::get<0>(aResult.ResolveValue()); if (NS_FAILED(rv)) { return GenericNonExclusivePromise::CreateAndReject( rv, __func__); } OpenRemoteDecoderManagerChildForProcess( std::get<1>(std::move(aResult.ResolveValue())), aLocation); return GenericNonExclusivePromise::CreateAndResolve( true, __func__); }); }); // Let's make sure this promise is also run on the managerThread to avoid // situations where it would be run on a threadpool thread. // During bug 1794988 this was happening when enabling Utility for audio on // Android when running the sequence of tests // dom/media/test/test_access_control.html // dom/media/test/test_arraybuffer.html // // We would have a launched utility process but the promises would not have // been cleared, so any subsequent tentative to perform audio decoding would // think the process is not yet ran and it would try to wait on the pending // promises. p = p->Then( managerThread, __func__, [aLocation]( const GenericNonExclusivePromise::ResolveOrRejectValue& aResult) { StaticMutexAutoLock lock(sLaunchMutex); sLaunchPromises[aLocation] = nullptr; return GenericNonExclusivePromise::CreateAndResolveOrReject(aResult, __func__); }); utilityLaunchPromise = p; return utilityLaunchPromise; } /* static */ TrackSupportSet RemoteDecoderManagerChild::GetTrackSupport( RemoteDecodeIn aLocation) { switch (aLocation) { case RemoteDecodeIn::GpuProcess: { return TrackSupportSet{TrackSupport::Video}; } case RemoteDecodeIn::RddProcess: { TrackSupportSet s{TrackSupport::Video}; // Only use RDD for audio decoding if we don't have the utility process. if (!StaticPrefs::media_utility_process_enabled()) { s += TrackSupport::Audio; } return s; } case RemoteDecodeIn::UtilityProcess_Generic: case RemoteDecodeIn::UtilityProcess_AppleMedia: case RemoteDecodeIn::UtilityProcess_WMF: return StaticPrefs::media_utility_process_enabled() ? TrackSupportSet{TrackSupport::Audio} : TrackSupportSet{TrackSupport::None}; case RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM: { TrackSupportSet s{TrackSupport::None}; #ifdef MOZ_WMF_MEDIA_ENGINE // When we enable the media engine, it would need both tracks to // synchronize the a/v playback. if (StaticPrefs::media_wmf_media_engine_enabled()) { s += TrackSupportSet{TrackSupport::Audio, TrackSupport::Video}; } #endif return s; } default: MOZ_ASSERT_UNREACHABLE("Undefined location!"); } return TrackSupportSet{TrackSupport::None}; } PRemoteDecoderChild* RemoteDecoderManagerChild::AllocPRemoteDecoderChild( const RemoteDecoderInfoIPDL& /* not used */, const CreateDecoderParams::OptionSet& aOptions, const Maybe& aIdentifier, const Maybe& aMediaEngineId, const Maybe& aTrackingId) { // RemoteDecoderModule is responsible for creating RemoteDecoderChild // classes. MOZ_ASSERT(false, "RemoteDecoderManagerChild cannot create " "RemoteDecoderChild classes"); return nullptr; } bool RemoteDecoderManagerChild::DeallocPRemoteDecoderChild( PRemoteDecoderChild* actor) { RemoteDecoderChild* child = static_cast(actor); child->IPDLActorDestroyed(); return true; } PMFMediaEngineChild* RemoteDecoderManagerChild::AllocPMFMediaEngineChild() { MOZ_ASSERT_UNREACHABLE( "RemoteDecoderManagerChild cannot create MFMediaEngineChild classes"); return nullptr; } bool RemoteDecoderManagerChild::DeallocPMFMediaEngineChild( PMFMediaEngineChild* actor) { #ifdef MOZ_WMF_MEDIA_ENGINE MFMediaEngineChild* child = static_cast(actor); child->IPDLActorDestroyed(); #endif return true; } PMFCDMChild* RemoteDecoderManagerChild::AllocPMFCDMChild(const nsAString&) { MOZ_ASSERT_UNREACHABLE( "RemoteDecoderManagerChild cannot create PMFContentDecryptionModuleChild " "classes"); return nullptr; } bool RemoteDecoderManagerChild::DeallocPMFCDMChild(PMFCDMChild* actor) { #ifdef MOZ_WMF_CDM static_cast(actor)->IPDLActorDestroyed(); #endif return true; } RemoteDecoderManagerChild::RemoteDecoderManagerChild(RemoteDecodeIn aLocation) : mLocation(aLocation) { MOZ_ASSERT(mLocation == RemoteDecodeIn::GpuProcess || mLocation == RemoteDecodeIn::RddProcess || mLocation == RemoteDecodeIn::UtilityProcess_Generic || mLocation == RemoteDecodeIn::UtilityProcess_AppleMedia || mLocation == RemoteDecodeIn::UtilityProcess_WMF || mLocation == RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM); } /* static */ void RemoteDecoderManagerChild::OpenRemoteDecoderManagerChildForProcess( Endpoint&& aEndpoint, RemoteDecodeIn aLocation) { nsCOMPtr managerThread = GetManagerThread(); if (!managerThread) { // We've been shutdown, bail. return; } MOZ_ASSERT(managerThread->IsOnCurrentThread()); // For GPU process, make sure we always dispatch everything in sRecreateTasks, // even if we fail since this is as close to being recreated as we will ever // be. auto runRecreateTasksIfNeeded = MakeScopeExit([aLocation]() { if (aLocation == RemoteDecodeIn::GpuProcess) { for (Runnable* task : *sRecreateTasks) { task->Run(); } sRecreateTasks->Clear(); } }); // Only create RemoteDecoderManagerChild, bind new endpoint and init // ipdl if: // 1) haven't init'd sRemoteDecoderManagerChildForProcesses[aLocation] // or // 2) if ActorDestroy was called meaning the other end of the ipc channel was // torn down // But for GPU process, we always recreate a new manager child. MOZ_ASSERT(aLocation != RemoteDecodeIn::SENTINEL); auto& remoteDecoderManagerChild = sRemoteDecoderManagerChildForProcesses[aLocation]; if (aLocation != RemoteDecodeIn::GpuProcess && remoteDecoderManagerChild && remoteDecoderManagerChild->CanSend()) { return; } remoteDecoderManagerChild = nullptr; if (aEndpoint.IsValid()) { RefPtr manager = new RemoteDecoderManagerChild(aLocation); if (aEndpoint.Bind(manager)) { remoteDecoderManagerChild = manager; } } } bool RemoteDecoderManagerChild::DeallocShmem(mozilla::ipc::Shmem& aShmem) { nsCOMPtr managerThread = GetManagerThread(); if (!managerThread) { return false; } if (!managerThread->IsOnCurrentThread()) { MOZ_ALWAYS_SUCCEEDS(managerThread->Dispatch(NS_NewRunnableFunction( "RemoteDecoderManagerChild::DeallocShmem", [self = RefPtr{this}, shmem = aShmem]() mutable { if (self->CanSend()) { self->PRemoteDecoderManagerChild::DeallocShmem(shmem); } }))); return true; } return PRemoteDecoderManagerChild::DeallocShmem(aShmem); } struct SurfaceDescriptorUserData { SurfaceDescriptorUserData(RemoteDecoderManagerChild* aAllocator, SurfaceDescriptor& aSD) : mAllocator(aAllocator), mSD(aSD) {} ~SurfaceDescriptorUserData() { DestroySurfaceDescriptor(mAllocator, &mSD); } RefPtr mAllocator; SurfaceDescriptor mSD; }; void DeleteSurfaceDescriptorUserData(void* aClosure) { SurfaceDescriptorUserData* sd = reinterpret_cast(aClosure); delete sd; } already_AddRefed RemoteDecoderManagerChild::Readback( const SurfaceDescriptorGPUVideo& aSD) { // We can't use NS_DispatchAndSpinEventLoopUntilComplete here since that will // spin the event loop while it waits. This function can be called from JS and // we don't want that to happen. nsCOMPtr managerThread = GetManagerThread(); if (!managerThread) { return nullptr; } SurfaceDescriptor sd; RefPtr task = NS_NewRunnableFunction("RemoteDecoderManagerChild::Readback", [&]() { if (CanSend()) { SendReadback(aSD, &sd); } }); SyncRunnable::DispatchToThread(managerThread, task); if (!IsSurfaceDescriptorValid(sd)) { return nullptr; } RefPtr source = GetSurfaceForDescriptor(sd); if (!source) { DestroySurfaceDescriptor(this, &sd); NS_WARNING("Failed to map SurfaceDescriptor in Readback"); return nullptr; } static UserDataKey sSurfaceDescriptor; source->AddUserData(&sSurfaceDescriptor, new SurfaceDescriptorUserData(this, sd), DeleteSurfaceDescriptorUserData); return source.forget(); } void RemoteDecoderManagerChild::DeallocateSurfaceDescriptor( const SurfaceDescriptorGPUVideo& aSD) { nsCOMPtr managerThread = GetManagerThread(); if (!managerThread) { return; } MOZ_ALWAYS_SUCCEEDS(managerThread->Dispatch(NS_NewRunnableFunction( "RemoteDecoderManagerChild::DeallocateSurfaceDescriptor", [ref = RefPtr{this}, sd = aSD]() { if (ref->CanSend()) { ref->SendDeallocateSurfaceDescriptorGPUVideo(sd); } }))); } void RemoteDecoderManagerChild::HandleFatalError(const char* aMsg) { dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid()); } void RemoteDecoderManagerChild::SetSupported( RemoteDecodeIn aLocation, const media::MediaCodecsSupported& aSupported) { switch (aLocation) { case RemoteDecodeIn::GpuProcess: case RemoteDecodeIn::RddProcess: case RemoteDecodeIn::UtilityProcess_AppleMedia: case RemoteDecodeIn::UtilityProcess_Generic: case RemoteDecodeIn::UtilityProcess_WMF: case RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM: { StaticMutexAutoLock lock(sProcessSupportedMutex); sProcessSupported[aLocation] = Some(aSupported); break; } default: MOZ_CRASH("Not to be used for any other process"); } } #undef LOG } // namespace mozilla