/* -*- 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 "RemoteDecoderManagerParent.h" #if XP_WIN # include #endif #include "ImageContainer.h" #include "PDMFactory.h" #include "RemoteAudioDecoder.h" #include "RemoteVideoDecoder.h" #include "VideoUtils.h" // for MediaThreadType #include "mozilla/RDDParent.h" #include "mozilla/RemoteDecodeUtils.h" #include "mozilla/ipc/UtilityProcessChild.h" #include "mozilla/SyncRunnable.h" #include "mozilla/gfx/GPUParent.h" #include "mozilla/ipc/Endpoint.h" #include "mozilla/layers/ImageDataSerializer.h" #include "mozilla/layers/VideoBridgeChild.h" #include "mozilla/layers/VideoBridgeParent.h" #ifdef MOZ_WMF_MEDIA_ENGINE # include "MFMediaEngineParent.h" #endif namespace mozilla { #define LOG(msg, ...) \ MOZ_LOG(gRemoteDecodeLog, LogLevel::Debug, (msg, ##__VA_ARGS__)) using namespace ipc; using namespace layers; using namespace gfx; StaticRefPtr sRemoteDecoderManagerParentThread; void RemoteDecoderManagerParent::StoreImage( const SurfaceDescriptorGPUVideo& aSD, Image* aImage, TextureClient* aTexture) { MOZ_ASSERT(OnManagerThread()); mImageMap[static_cast(aSD).handle()] = aImage; mTextureMap[static_cast(aSD).handle()] = aTexture; } class RemoteDecoderManagerThreadShutdownObserver : public nsIObserver { virtual ~RemoteDecoderManagerThreadShutdownObserver() = default; public: RemoteDecoderManagerThreadShutdownObserver() = default; NS_DECL_ISUPPORTS NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) override { MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0); RemoteDecoderManagerParent::ShutdownVideoBridge(); RemoteDecoderManagerParent::ShutdownThreads(); return NS_OK; } }; NS_IMPL_ISUPPORTS(RemoteDecoderManagerThreadShutdownObserver, nsIObserver); bool RemoteDecoderManagerParent::StartupThreads() { MOZ_ASSERT(NS_IsMainThread()); if (sRemoteDecoderManagerParentThread) { return true; } nsCOMPtr observerService = services::GetObserverService(); if (!observerService) { return false; } sRemoteDecoderManagerParentThread = TaskQueue::Create( GetMediaThreadPool(MediaThreadType::SUPERVISOR), "RemVidParent"); if (XRE_IsGPUProcess()) { MOZ_ALWAYS_SUCCEEDS( sRemoteDecoderManagerParentThread->Dispatch(NS_NewRunnableFunction( "RemoteDecoderManagerParent::StartupThreads", []() { layers::VideoBridgeChild::StartupForGPUProcess(); }))); } auto* obs = new RemoteDecoderManagerThreadShutdownObserver(); observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); return true; } void RemoteDecoderManagerParent::ShutdownThreads() { sRemoteDecoderManagerParentThread->BeginShutdown(); sRemoteDecoderManagerParentThread->AwaitShutdownAndIdle(); sRemoteDecoderManagerParentThread = nullptr; } /* static */ void RemoteDecoderManagerParent::ShutdownVideoBridge() { if (sRemoteDecoderManagerParentThread) { RefPtr task = NS_NewRunnableFunction( "RemoteDecoderManagerParent::ShutdownVideoBridge", []() { VideoBridgeParent::Shutdown(); VideoBridgeChild::Shutdown(); }); SyncRunnable::DispatchToThread(sRemoteDecoderManagerParentThread, task); } } bool RemoteDecoderManagerParent::OnManagerThread() { return sRemoteDecoderManagerParentThread->IsOnCurrentThread(); } PDMFactory& RemoteDecoderManagerParent::EnsurePDMFactory() { MOZ_ASSERT(OnManagerThread()); if (!mPDMFactory) { mPDMFactory = MakeRefPtr(); } return *mPDMFactory; } bool RemoteDecoderManagerParent::CreateForContent( Endpoint&& aEndpoint) { MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_RDD || XRE_GetProcessType() == GeckoProcessType_Utility || XRE_GetProcessType() == GeckoProcessType_GPU); MOZ_ASSERT(NS_IsMainThread()); if (!StartupThreads()) { return false; } RefPtr parent = new RemoteDecoderManagerParent(sRemoteDecoderManagerParentThread); RefPtr task = NewRunnableMethod&&>( "dom::RemoteDecoderManagerParent::Open", parent, &RemoteDecoderManagerParent::Open, std::move(aEndpoint)); MOZ_ALWAYS_SUCCEEDS( sRemoteDecoderManagerParentThread->Dispatch(task.forget())); return true; } bool RemoteDecoderManagerParent::CreateVideoBridgeToOtherProcess( Endpoint&& aEndpoint) { LOG("Create video bridge"); // We never want to decode in the GPU process, but output // frames to the parent process. MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_RDD || XRE_GetProcessType() == GeckoProcessType_Utility); #ifdef MOZ_WMF_MEDIA_ENGINE MOZ_ASSERT_IF( XRE_GetProcessType() == GeckoProcessType_Utility, GetCurrentSandboxingKind() == SandboxingKind::MF_MEDIA_ENGINE_CDM); #endif MOZ_ASSERT(NS_IsMainThread()); if (!StartupThreads()) { return false; } RefPtr task = NewRunnableFunction("gfx::VideoBridgeChild::Open", &VideoBridgeChild::Open, std::move(aEndpoint)); MOZ_ALWAYS_SUCCEEDS( sRemoteDecoderManagerParentThread->Dispatch(task.forget())); return true; } RemoteDecoderManagerParent::RemoteDecoderManagerParent( nsISerialEventTarget* aThread) : mThread(aThread) { MOZ_COUNT_CTOR(RemoteDecoderManagerParent); auto& registrar = XRE_IsGPUProcess() ? GPUParent::GetSingleton()->AsyncShutdownService() : XRE_IsUtilityProcess() ? UtilityProcessChild::GetSingleton()->AsyncShutdownService() : RDDParent::GetSingleton()->AsyncShutdownService(); registrar.Register(this); } RemoteDecoderManagerParent::~RemoteDecoderManagerParent() { MOZ_COUNT_DTOR(RemoteDecoderManagerParent); auto& registrar = XRE_IsGPUProcess() ? GPUParent::GetSingleton()->AsyncShutdownService() : XRE_IsUtilityProcess() ? UtilityProcessChild::GetSingleton()->AsyncShutdownService() : RDDParent::GetSingleton()->AsyncShutdownService(); registrar.Deregister(this); } void RemoteDecoderManagerParent::ActorDestroy( mozilla::ipc::IProtocol::ActorDestroyReason) { mThread = nullptr; } PRemoteDecoderParent* RemoteDecoderManagerParent::AllocPRemoteDecoderParent( const RemoteDecoderInfoIPDL& aRemoteDecoderInfo, const CreateDecoderParams::OptionSet& aOptions, const Maybe& aIdentifier, const Maybe& aMediaEngineId, const Maybe& aTrackingId) { RefPtr decodeTaskQueue = TaskQueue::Create(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER), "RemoteVideoDecoderParent::mDecodeTaskQueue"); if (aRemoteDecoderInfo.type() == RemoteDecoderInfoIPDL::TVideoDecoderInfoIPDL) { const VideoDecoderInfoIPDL& decoderInfo = aRemoteDecoderInfo.get_VideoDecoderInfoIPDL(); return new RemoteVideoDecoderParent( this, decoderInfo.videoInfo(), decoderInfo.framerate(), aOptions, aIdentifier, sRemoteDecoderManagerParentThread, decodeTaskQueue, aMediaEngineId, aTrackingId); } if (aRemoteDecoderInfo.type() == RemoteDecoderInfoIPDL::TAudioInfo) { return new RemoteAudioDecoderParent( this, aRemoteDecoderInfo.get_AudioInfo(), aOptions, sRemoteDecoderManagerParentThread, decodeTaskQueue, aMediaEngineId); } MOZ_CRASH("unrecognized type of RemoteDecoderInfoIPDL union"); return nullptr; } bool RemoteDecoderManagerParent::DeallocPRemoteDecoderParent( PRemoteDecoderParent* actor) { RemoteDecoderParent* parent = static_cast(actor); parent->Destroy(); return true; } PMFMediaEngineParent* RemoteDecoderManagerParent::AllocPMFMediaEngineParent() { #ifdef MOZ_WMF_MEDIA_ENGINE return new MFMediaEngineParent(this, sRemoteDecoderManagerParentThread); #else return nullptr; #endif } bool RemoteDecoderManagerParent::DeallocPMFMediaEngineParent( PMFMediaEngineParent* actor) { #ifdef MOZ_WMF_MEDIA_ENGINE MFMediaEngineParent* parent = static_cast(actor); parent->Destroy(); #endif return true; } void RemoteDecoderManagerParent::Open( Endpoint&& aEndpoint) { if (!aEndpoint.Bind(this)) { // We can't recover from this. MOZ_CRASH("Failed to bind RemoteDecoderManagerParent to endpoint"); } AddRef(); } void RemoteDecoderManagerParent::ActorDealloc() { Release(); } mozilla::ipc::IPCResult RemoteDecoderManagerParent::RecvReadback( const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult) { const SurfaceDescriptorRemoteDecoder& sd = aSD; RefPtr image = mImageMap[sd.handle()]; if (!image) { *aResult = null_t(); return IPC_OK(); } RefPtr source = image->GetAsSourceSurface(); if (!source) { *aResult = null_t(); return IPC_OK(); } SurfaceFormat format = source->GetFormat(); IntSize size = source->GetSize(); size_t length = ImageDataSerializer::ComputeRGBBufferSize(size, format); Shmem buffer; if (!length || !AllocShmem(length, &buffer)) { *aResult = null_t(); return IPC_OK(); } RefPtr dt = Factory::CreateDrawTargetForData( gfx::BackendType::CAIRO, buffer.get(), size, ImageDataSerializer::ComputeRGBStride(format, size.width), format); if (!dt) { DeallocShmem(buffer); *aResult = null_t(); return IPC_OK(); } dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint()); dt->Flush(); *aResult = SurfaceDescriptorBuffer(RGBDescriptor(size, format), MemoryOrShmem(std::move(buffer))); return IPC_OK(); } mozilla::ipc::IPCResult RemoteDecoderManagerParent::RecvDeallocateSurfaceDescriptorGPUVideo( const SurfaceDescriptorGPUVideo& aSD) { MOZ_ASSERT(OnManagerThread()); const SurfaceDescriptorRemoteDecoder& sd = aSD; mImageMap.erase(sd.handle()); mTextureMap.erase(sd.handle()); return IPC_OK(); } void RemoteDecoderManagerParent::DeallocateSurfaceDescriptor( const SurfaceDescriptorGPUVideo& aSD) { if (!OnManagerThread()) { MOZ_ALWAYS_SUCCEEDS( sRemoteDecoderManagerParentThread->Dispatch(NS_NewRunnableFunction( "RemoteDecoderManagerParent::DeallocateSurfaceDescriptor", [ref = RefPtr{this}, sd = aSD]() { ref->RecvDeallocateSurfaceDescriptorGPUVideo(sd); }))); } else { RecvDeallocateSurfaceDescriptorGPUVideo(aSD); } } #undef LOG } // namespace mozilla