summaryrefslogtreecommitdiffstats
path: root/dom/media/ipc/RemoteDecoderManagerParent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/ipc/RemoteDecoderManagerParent.cpp')
-rw-r--r--dom/media/ipc/RemoteDecoderManagerParent.cpp378
1 files changed, 378 insertions, 0 deletions
diff --git a/dom/media/ipc/RemoteDecoderManagerParent.cpp b/dom/media/ipc/RemoteDecoderManagerParent.cpp
new file mode 100644
index 0000000000..ffb3b75dbe
--- /dev/null
+++ b/dom/media/ipc/RemoteDecoderManagerParent.cpp
@@ -0,0 +1,378 @@
+/* -*- 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();
+ }
+
+ // Let's try reading directly into the shmem first to avoid extra copies.
+ 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());
+ }
+
+ if (rv != NS_ERROR_NOT_IMPLEMENTED) {
+ *aResult = null_t();
+ return IPC_OK();
+ }
+
+ // Fallback to reading to a SourceSurface and copying that into a shmem.
+ RefPtr<SourceSurface> 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<DrawTarget> dt = Factory::CreateDrawTargetForData(
+ gfx::BackendType::CAIRO, buffer.get<uint8_t>(), 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