Files
tubestation/dom/media/ipc/RemoteDecoderManagerParent.cpp
Andrew Osmond 09959678c3 Bug 1965093 - Add Image::BuildSurfaceDescriptorBuffer fallback implementation. r=gfx-reviewers,jnicol
This patch implements BuildSurfaceDescriptorBuffer for the Image
base class. While it does not have the performance benefits of the
more specific implementations in the derived classes, it allows the
callers to not worry about the edge cases where GetSourceSurface is
available.

Differential Revision: https://phabricator.services.mozilla.com/D248352
2025-05-09 23:35:56 +00:00

343 lines
11 KiB
C++

/* -*- 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 <objbase.h>
#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"
#include "nsIObserverService.h"
#ifdef MOZ_WMF_MEDIA_ENGINE
# include "MFMediaEngineParent.h"
#endif
#ifdef MOZ_WMF_CDM
# include "MFCDMParent.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<TaskQueue> sRemoteDecoderManagerParentThread;
void RemoteDecoderManagerParent::StoreImage(
const SurfaceDescriptorGPUVideo& aSD, Image* aImage,
TextureClient* aTexture) {
MOZ_ASSERT(OnManagerThread());
mImageMap[static_cast<SurfaceDescriptorRemoteDecoder>(aSD).handle()] = aImage;
mTextureMap[static_cast<SurfaceDescriptorRemoteDecoder>(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<nsIObserverService> 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<Runnable> task = NS_NewRunnableFunction(
"RemoteDecoderManagerParent::ShutdownVideoBridge",
[]() { VideoBridgeChild::Shutdown(); });
SyncRunnable::DispatchToThread(sRemoteDecoderManagerParentThread, task);
}
}
bool RemoteDecoderManagerParent::OnManagerThread() {
return sRemoteDecoderManagerParentThread->IsOnCurrentThread();
}
PDMFactory& RemoteDecoderManagerParent::EnsurePDMFactory() {
MOZ_ASSERT(OnManagerThread());
if (!mPDMFactory) {
mPDMFactory = MakeRefPtr<PDMFactory>();
}
return *mPDMFactory;
}
bool RemoteDecoderManagerParent::CreateForContent(
Endpoint<PRemoteDecoderManagerParent>&& aEndpoint,
dom::ContentParentId aChildId) {
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_RDD ||
XRE_GetProcessType() == GeckoProcessType_Utility ||
XRE_GetProcessType() == GeckoProcessType_GPU);
MOZ_ASSERT(NS_IsMainThread());
if (!StartupThreads()) {
return false;
}
RefPtr<RemoteDecoderManagerParent> parent = new RemoteDecoderManagerParent(
sRemoteDecoderManagerParentThread, aChildId);
RefPtr<Runnable> task =
NewRunnableMethod<Endpoint<PRemoteDecoderManagerParent>&&>(
"dom::RemoteDecoderManagerParent::Open", parent,
&RemoteDecoderManagerParent::Open, std::move(aEndpoint));
MOZ_ALWAYS_SUCCEEDS(
sRemoteDecoderManagerParentThread->Dispatch(task.forget()));
return true;
}
bool RemoteDecoderManagerParent::CreateVideoBridgeToOtherProcess(
Endpoint<PVideoBridgeChild>&& 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<Runnable> task =
NewRunnableFunction("gfx::VideoBridgeChild::Open",
&VideoBridgeChild::Open, std::move(aEndpoint));
MOZ_ALWAYS_SUCCEEDS(
sRemoteDecoderManagerParentThread->Dispatch(task.forget()));
return true;
}
RemoteDecoderManagerParent::RemoteDecoderManagerParent(
nsISerialEventTarget* aThread, dom::ContentParentId aContentId)
: mThread(aThread), mContentId(aContentId) {
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<layers::TextureFactoryIdentifier>& aIdentifier,
const Maybe<uint64_t>& aMediaEngineId,
const Maybe<TrackingId>& aTrackingId) {
RefPtr<TaskQueue> 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<RemoteDecoderParent*>(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<MFMediaEngineParent*>(actor);
parent->Destroy();
#endif
return true;
}
PMFCDMParent* RemoteDecoderManagerParent::AllocPMFCDMParent(
const nsAString& aKeySystem) {
#ifdef MOZ_WMF_CDM
return new MFCDMParent(aKeySystem, this, sRemoteDecoderManagerParentThread);
#else
return nullptr;
#endif
}
bool RemoteDecoderManagerParent::DeallocPMFCDMParent(PMFCDMParent* actor) {
#ifdef MOZ_WMF_CDM
static_cast<MFCDMParent*>(actor)->Destroy();
#endif
return true;
}
void RemoteDecoderManagerParent::Open(
Endpoint<PRemoteDecoderManagerParent>&& aEndpoint) {
if (!aEndpoint.Bind(this)) {
// We can't recover from this.
MOZ_CRASH("Failed to bind RemoteDecoderManagerParent to endpoint");
}
}
mozilla::ipc::IPCResult RemoteDecoderManagerParent::RecvReadback(
const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult) {
const SurfaceDescriptorRemoteDecoder& sd = aSD;
RefPtr<Image> image = mImageMap[sd.handle()];
if (!image) {
*aResult = null_t();
return IPC_OK();
}
// Read directly into the shmem to avoid extra copies, if possible.
SurfaceDescriptorBuffer sdb;
nsresult rv = image->BuildSurfaceDescriptorBuffer(
sdb, Image::BuildSdbFlags::RgbOnly, [&](uint32_t aBufferSize) {
Shmem buffer;
if (!AllocShmem(aBufferSize, &buffer)) {
return MemoryOrShmem();
}
return MemoryOrShmem(std::move(buffer));
});
if (NS_SUCCEEDED(rv)) {
*aResult = std::move(sdb);
return IPC_OK();
}
if (sdb.data().type() == MemoryOrShmem::TShmem) {
DeallocShmem(sdb.data().get_Shmem());
}
*aResult = null_t();
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