summaryrefslogtreecommitdiffstats
path: root/dom/media/ipc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/media/ipc/MediaIPCUtils.h246
-rw-r--r--dom/media/ipc/PMediaDecoderParams.ipdlh21
-rw-r--r--dom/media/ipc/PRDD.ipdl68
-rw-r--r--dom/media/ipc/PRemoteDecoder.ipdl77
-rw-r--r--dom/media/ipc/PRemoteDecoderManager.ipdl46
-rw-r--r--dom/media/ipc/RDDChild.cpp180
-rw-r--r--dom/media/ipc/RDDChild.h67
-rw-r--r--dom/media/ipc/RDDParent.cpp304
-rw-r--r--dom/media/ipc/RDDParent.h64
-rw-r--r--dom/media/ipc/RDDProcessHost.cpp311
-rw-r--r--dom/media/ipc/RDDProcessHost.h163
-rw-r--r--dom/media/ipc/RDDProcessImpl.cpp82
-rw-r--r--dom/media/ipc/RDDProcessImpl.h41
-rw-r--r--dom/media/ipc/RDDProcessManager.cpp373
-rw-r--r--dom/media/ipc/RDDProcessManager.h116
-rw-r--r--dom/media/ipc/RemoteAudioDecoder.cpp117
-rw-r--r--dom/media/ipc/RemoteAudioDecoder.h51
-rw-r--r--dom/media/ipc/RemoteDecoderChild.cpp275
-rw-r--r--dom/media/ipc/RemoteDecoderChild.h84
-rw-r--r--dom/media/ipc/RemoteDecoderManagerChild.cpp619
-rw-r--r--dom/media/ipc/RemoteDecoderManagerChild.h132
-rw-r--r--dom/media/ipc/RemoteDecoderManagerParent.cpp295
-rw-r--r--dom/media/ipc/RemoteDecoderManagerParent.h86
-rw-r--r--dom/media/ipc/RemoteDecoderModule.cpp76
-rw-r--r--dom/media/ipc/RemoteDecoderModule.h50
-rw-r--r--dom/media/ipc/RemoteDecoderParent.cpp228
-rw-r--r--dom/media/ipc/RemoteDecoderParent.h68
-rw-r--r--dom/media/ipc/RemoteImageHolder.cpp166
-rw-r--r--dom/media/ipc/RemoteImageHolder.h64
-rw-r--r--dom/media/ipc/RemoteMediaData.cpp337
-rw-r--r--dom/media/ipc/RemoteMediaData.h392
-rw-r--r--dom/media/ipc/RemoteMediaDataDecoder.cpp140
-rw-r--r--dom/media/ipc/RemoteMediaDataDecoder.h62
-rw-r--r--dom/media/ipc/RemoteVideoDecoder.cpp287
-rw-r--r--dom/media/ipc/RemoteVideoDecoder.h79
-rw-r--r--dom/media/ipc/ShmemRecycleAllocator.h60
-rw-r--r--dom/media/ipc/moz.build65
37 files changed, 5892 insertions, 0 deletions
diff --git a/dom/media/ipc/MediaIPCUtils.h b/dom/media/ipc/MediaIPCUtils.h
new file mode 100644
index 0000000000..b940a3af8b
--- /dev/null
+++ b/dom/media/ipc/MediaIPCUtils.h
@@ -0,0 +1,246 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_media_MediaIPCUtils_h
+#define mozilla_dom_media_MediaIPCUtils_h
+
+#include "DecoderDoctorDiagnostics.h"
+#include "PlatformDecoderModule.h"
+#include "ipc/EnumSerializer.h"
+#include "mozilla/EnumSet.h"
+#include "mozilla/GfxMessageUtils.h"
+#include "mozilla/gfx/Rect.h"
+
+namespace IPC {
+template <>
+struct ParamTraits<mozilla::VideoInfo> {
+ typedef mozilla::VideoInfo paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ // TrackInfo
+ WriteParam(aMsg, aParam.mMimeType);
+
+ // VideoInfo
+ WriteParam(aMsg, aParam.mDisplay);
+ WriteParam(aMsg, aParam.mStereoMode);
+ WriteParam(aMsg, aParam.mImage);
+ WriteParam(aMsg, aParam.mImageRect);
+ WriteParam(aMsg, *aParam.mCodecSpecificConfig);
+ WriteParam(aMsg, *aParam.mExtraData);
+ WriteParam(aMsg, aParam.mRotation);
+ WriteParam(aMsg, aParam.mColorDepth);
+ WriteParam(aMsg, aParam.mColorSpace);
+ WriteParam(aMsg, aParam.mColorRange);
+ WriteParam(aMsg, aParam.HasAlpha());
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ mozilla::gfx::IntRect imageRect;
+ bool alphaPresent;
+ if (ReadParam(aMsg, aIter, &aResult->mMimeType) &&
+ ReadParam(aMsg, aIter, &aResult->mDisplay) &&
+ ReadParam(aMsg, aIter, &aResult->mStereoMode) &&
+ ReadParam(aMsg, aIter, &aResult->mImage) &&
+ ReadParam(aMsg, aIter, &aResult->mImageRect) &&
+ ReadParam(aMsg, aIter, aResult->mCodecSpecificConfig.get()) &&
+ ReadParam(aMsg, aIter, aResult->mExtraData.get()) &&
+ ReadParam(aMsg, aIter, &aResult->mRotation) &&
+ ReadParam(aMsg, aIter, &aResult->mColorDepth) &&
+ ReadParam(aMsg, aIter, &aResult->mColorSpace) &&
+ ReadParam(aMsg, aIter, &aResult->mColorRange) &&
+ ReadParam(aMsg, aIter, &alphaPresent)) {
+ aResult->SetAlpha(alphaPresent);
+ return true;
+ }
+ return false;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::TrackInfo::TrackType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::TrackInfo::TrackType,
+ mozilla::TrackInfo::TrackType::kUndefinedTrack,
+ mozilla::TrackInfo::TrackType::kTextTrack> {};
+
+template <>
+struct ParamTraits<mozilla::VideoInfo::Rotation>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::VideoInfo::Rotation, mozilla::VideoInfo::Rotation::kDegree_0,
+ mozilla::VideoInfo::Rotation::kDegree_270> {};
+
+template <>
+struct ParamTraits<mozilla::MediaByteBuffer>
+ : public ParamTraits<nsTArray<uint8_t>> {
+ typedef mozilla::MediaByteBuffer paramType;
+};
+
+template <>
+struct ParamTraits<mozilla::AudioInfo> {
+ typedef mozilla::AudioInfo paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ // TrackInfo
+ WriteParam(aMsg, aParam.mMimeType);
+
+ // AudioInfo
+ WriteParam(aMsg, aParam.mRate);
+ WriteParam(aMsg, aParam.mChannels);
+ WriteParam(aMsg, aParam.mChannelMap);
+ WriteParam(aMsg, aParam.mBitDepth);
+ WriteParam(aMsg, aParam.mProfile);
+ WriteParam(aMsg, aParam.mExtendedProfile);
+ WriteParam(aMsg, *aParam.mCodecSpecificConfig);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ if (ReadParam(aMsg, aIter, &aResult->mMimeType) &&
+ ReadParam(aMsg, aIter, &aResult->mRate) &&
+ ReadParam(aMsg, aIter, &aResult->mChannels) &&
+ ReadParam(aMsg, aIter, &aResult->mChannelMap) &&
+ ReadParam(aMsg, aIter, &aResult->mBitDepth) &&
+ ReadParam(aMsg, aIter, &aResult->mProfile) &&
+ ReadParam(aMsg, aIter, &aResult->mExtendedProfile) &&
+ ReadParam(aMsg, aIter, aResult->mCodecSpecificConfig.get())) {
+ return true;
+ }
+ return false;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::MediaDataDecoder::ConversionRequired>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::MediaDataDecoder::ConversionRequired,
+ mozilla::MediaDataDecoder::ConversionRequired(0),
+ mozilla::MediaDataDecoder::ConversionRequired(
+ mozilla::MediaDataDecoder::ConversionRequired::kNeedAnnexB)> {};
+
+template <>
+struct ParamTraits<mozilla::media::TimeUnit> {
+ typedef mozilla::media::TimeUnit paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.IsValid());
+ WriteParam(aMsg, aParam.IsValid() ? aParam.ToMicroseconds() : 0);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ bool valid;
+ int64_t value;
+ if (ReadParam(aMsg, aIter, &valid) && ReadParam(aMsg, aIter, &value)) {
+ if (!valid) {
+ *aResult = mozilla::media::TimeUnit::Invalid();
+ } else {
+ *aResult = mozilla::media::TimeUnit::FromMicroseconds(value);
+ }
+ return true;
+ }
+ return false;
+ };
+};
+
+template <>
+struct ParamTraits<mozilla::media::TimeInterval> {
+ typedef mozilla::media::TimeInterval paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mStart);
+ WriteParam(aMsg, aParam.mEnd);
+ WriteParam(aMsg, aParam.mFuzz);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ if (ReadParam(aMsg, aIter, &aResult->mStart) &&
+ ReadParam(aMsg, aIter, &aResult->mEnd) &&
+ ReadParam(aMsg, aIter, &aResult->mFuzz)) {
+ return true;
+ }
+ return false;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::MediaResult> {
+ typedef mozilla::MediaResult paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.Code());
+ WriteParam(aMsg, aParam.Message());
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ nsresult result;
+ nsCString message;
+ if (ReadParam(aMsg, aIter, &result) && ReadParam(aMsg, aIter, &message)) {
+ *aResult = paramType(result, std::move(message));
+ return true;
+ }
+ return false;
+ };
+};
+
+template <>
+struct ParamTraits<mozilla::DecoderDoctorDiagnostics> {
+ typedef mozilla::DecoderDoctorDiagnostics paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mDiagnosticsType);
+ WriteParam(aMsg, aParam.mFormat);
+ WriteParam(aMsg, aParam.mFlags);
+ WriteParam(aMsg, aParam.mEvent);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ if (ReadParam(aMsg, aIter, &aResult->mDiagnosticsType) &&
+ ReadParam(aMsg, aIter, &aResult->mFormat) &&
+ ReadParam(aMsg, aIter, &aResult->mFlags) &&
+ ReadParam(aMsg, aIter, &aResult->mEvent)) {
+ return true;
+ }
+ return false;
+ };
+};
+
+template <>
+struct ParamTraits<mozilla::DecoderDoctorDiagnostics::DiagnosticsType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::DecoderDoctorDiagnostics::DiagnosticsType,
+ mozilla::DecoderDoctorDiagnostics::DiagnosticsType::eUnsaved,
+ mozilla::DecoderDoctorDiagnostics::DiagnosticsType::eDecodeWarning> {
+};
+
+template <>
+struct ParamTraits<mozilla::DecoderDoctorEvent> {
+ typedef mozilla::DecoderDoctorEvent paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ int domain = aParam.mDomain;
+ WriteParam(aMsg, domain);
+ WriteParam(aMsg, aParam.mResult);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ int domain = 0;
+ if (ReadParam(aMsg, aIter, &domain) &&
+ ReadParam(aMsg, aIter, &aResult->mResult)) {
+ aResult->mDomain = paramType::Domain(domain);
+ return true;
+ }
+ return false;
+ };
+};
+
+} // namespace IPC
+
+#endif // mozilla_dom_media_MediaIPCUtils_h
diff --git a/dom/media/ipc/PMediaDecoderParams.ipdlh b/dom/media/ipc/PMediaDecoderParams.ipdlh
new file mode 100644
index 0000000000..636b91bafa
--- /dev/null
+++ b/dom/media/ipc/PMediaDecoderParams.ipdlh
@@ -0,0 +1,21 @@
+/* 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 "mozilla/dom/MediaIPCUtils.h";
+
+using mozilla::media::TimeUnit from "TimeUnits.h";
+
+namespace mozilla {
+
+// used for both SendInput/RecvInput and ProcessDecodedData/RecvOutput
+struct MediaDataIPDL
+{
+ int64_t offset;
+ TimeUnit time;
+ TimeUnit timecode;
+ TimeUnit duration;
+ bool keyframe;
+};
+
+} // namespace mozilla
diff --git a/dom/media/ipc/PRDD.ipdl b/dom/media/ipc/PRDD.ipdl
new file mode 100644
index 0000000000..e2898f4cab
--- /dev/null
+++ b/dom/media/ipc/PRDD.ipdl
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 GraphicsMessages;
+include MemoryReportTypes;
+include PrefsTypes;
+
+include protocol PProfiler;
+include protocol PRemoteDecoderManager;
+include protocol PVideoBridge;
+
+using mozilla::dom::NativeThreadId from "mozilla/dom/NativeThreadId.h";
+using moveonly mozilla::UntrustedModulesData from "mozilla/UntrustedModulesData.h";
+using moveonly mozilla::ModulePaths from "mozilla/UntrustedModulesData.h";
+using moveonly mozilla::ModulesMapResult from "mozilla/UntrustedModulesData.h";
+using mozilla::PDMFactory::MediaCodecsSupported from "PDMFactory.h";
+
+namespace mozilla {
+
+// This protocol allows the UI process to talk to the RDD
+// (RemoteDataDecoder) process. There is one instance of this protocol,
+// with the RDDParent living on the main thread of the RDD process and
+// the RDDChild living on the main thread of the UI process.
+protocol PRDD
+{
+parent:
+
+ async Init(GfxVarUpdate[] vars, FileDescriptor? sandboxBroker,
+ bool canRecordReleaseTelemetry);
+
+ async InitProfiler(Endpoint<PProfilerChild> endpoint);
+
+ async NewContentRemoteDecoderManager(
+ Endpoint<PRemoteDecoderManagerParent> endpoint);
+
+ async RequestMemoryReport(uint32_t generation,
+ bool anonymize,
+ bool minimizeMemoryUsage,
+ FileDescriptor? DMDFile)
+ returns (uint32_t aGeneration);
+
+ async PreferenceUpdate(Pref pref);
+
+ async UpdateVar(GfxVarUpdate var);
+
+ async InitVideoBridge(Endpoint<PVideoBridgeChild> endpoint,
+ bool createHardwareDevice,
+ ContentDeviceData contentDeviceData);
+
+ async GetUntrustedModulesData() returns (UntrustedModulesData? data);
+
+child:
+
+ async InitCrashReporter(NativeThreadId threadId);
+
+ async AddMemoryReport(MemoryReport aReport);
+
+ async GetModulesTrust(ModulePaths aModPaths, bool aRunAtNormalPriority)
+ returns (ModulesMapResult? modMapResult);
+
+ // Update the cached list of codec supported following a check in the
+ // RDD parent.
+ async UpdateMediaCodecsSupported(MediaCodecsSupported aSupported);
+};
+
+} // namespace mozilla
diff --git a/dom/media/ipc/PRemoteDecoder.ipdl b/dom/media/ipc/PRemoteDecoder.ipdl
new file mode 100644
index 0000000000..f71b08cda4
--- /dev/null
+++ b/dom/media/ipc/PRemoteDecoder.ipdl
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "mozilla/dom/MediaIPCUtils.h";
+
+include protocol PRemoteDecoderManager;
+
+using mozilla::MediaDataDecoder::ConversionRequired from "PlatformDecoderModule.h";
+using mozilla::TrackInfo::TrackType from "MediaInfo.h";
+using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+using mozilla::MediaResult from "MediaResult.h";
+using refcounted class mozilla::ArrayOfRemoteMediaRawData from "mozilla/RemoteMediaData.h";
+using refcounted class mozilla::ArrayOfRemoteAudioData from "mozilla/RemoteMediaData.h";
+using refcounted class mozilla::ArrayOfRemoteVideoData from "mozilla/RemoteMediaData.h";
+include PMediaDecoderParams;
+include LayersSurfaces;
+
+namespace mozilla {
+
+union DecodedOutputIPDL
+{
+ ArrayOfRemoteAudioData;
+ ArrayOfRemoteVideoData;
+};
+
+struct InitCompletionIPDL
+{
+ TrackType type;
+ nsCString decoderDescription;
+ bool hardware;
+ nsCString hardwareReason;
+ ConversionRequired conversion;
+};
+
+union InitResultIPDL
+{
+ MediaResult;
+ InitCompletionIPDL;
+};
+
+union DecodeResultIPDL
+{
+ MediaResult;
+ DecodedOutputIPDL;
+};
+
+// This protocol provides a way to use MediaDataDecoder across processes.
+// The parent side currently is only implemented to work with
+// RemoteDecoderModule or WindowsMediaFoundation.
+// The child side runs in the content process, and the parent side runs
+// in the RDD process or the GPU process. We run a separate IPDL thread
+// for both sides.
+async protocol PRemoteDecoder
+{
+ manager PRemoteDecoderManager;
+parent:
+ async Construct() returns (MediaResult result);
+
+ async Init() returns (InitResultIPDL result);
+
+ // Each output may include a SurfaceDescriptorGPUVideo that represents the decoded
+ // frame. This SurfaceDescriptor can be used on the Layers IPDL protocol, but
+ // must be released explicitly using DeallocateSurfaceDescriptorGPUVideo
+ // on the manager protocol.
+ async Decode(ArrayOfRemoteMediaRawData data) returns (DecodeResultIPDL result);
+ async Flush() returns (MediaResult error);
+ async Drain() returns (DecodeResultIPDL result);
+ async Shutdown() returns (bool unused);
+ // To clear the threshold, call with INT64_MIN.
+ async SetSeekThreshold(TimeUnit time);
+
+ async __delete__();
+};
+
+} // namespace mozilla
diff --git a/dom/media/ipc/PRemoteDecoderManager.ipdl b/dom/media/ipc/PRemoteDecoderManager.ipdl
new file mode 100644
index 0000000000..1e8f4eafe9
--- /dev/null
+++ b/dom/media/ipc/PRemoteDecoderManager.ipdl
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 protocol PTexture;
+include protocol PRemoteDecoder;
+include LayersSurfaces;
+include "mozilla/dom/MediaIPCUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+
+using VideoInfo from "MediaInfo.h";
+using AudioInfo from "MediaInfo.h";
+using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
+using mozilla::CreateDecoderParams::OptionSet from "PlatformDecoderModule.h";
+using mozilla::DecoderDoctorDiagnostics from "DecoderDoctorDiagnostics.h";
+
+namespace mozilla {
+
+struct VideoDecoderInfoIPDL
+{
+ VideoInfo videoInfo;
+ float framerate;
+};
+
+union RemoteDecoderInfoIPDL
+{
+ AudioInfo;
+ VideoDecoderInfoIPDL;
+};
+
+sync protocol PRemoteDecoderManager
+{
+ manages PRemoteDecoder;
+
+parent:
+ async PRemoteDecoder(RemoteDecoderInfoIPDL info,
+ OptionSet options,
+ TextureFactoryIdentifier? identifier);
+
+ sync Readback(SurfaceDescriptorGPUVideo sd) returns (SurfaceDescriptor aResult);
+
+ async DeallocateSurfaceDescriptorGPUVideo(SurfaceDescriptorGPUVideo sd);
+};
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RDDChild.cpp b/dom/media/ipc/RDDChild.cpp
new file mode 100644
index 0000000000..5d51a49b30
--- /dev/null
+++ b/dom/media/ipc/RDDChild.cpp
@@ -0,0 +1,180 @@
+/* -*- 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 "RDDChild.h"
+
+#include "mozilla/RDDProcessManager.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/MemoryReportRequest.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/ipc/CrashReporterHost.h"
+#include "mozilla/ipc/Endpoint.h"
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+# include "mozilla/SandboxBroker.h"
+# include "mozilla/SandboxBrokerPolicyFactory.h"
+#endif
+
+#include "mozilla/Telemetry.h"
+
+#if defined(XP_WIN)
+# include "mozilla/WinDllServices.h"
+#endif
+
+#ifdef MOZ_GECKO_PROFILER
+# include "ProfilerParent.h"
+#endif
+#include "RDDProcessHost.h"
+
+namespace mozilla {
+
+using namespace layers;
+using namespace gfx;
+
+RDDChild::RDDChild(RDDProcessHost* aHost) : mHost(aHost) {
+ MOZ_COUNT_CTOR(RDDChild);
+}
+
+RDDChild::~RDDChild() { MOZ_COUNT_DTOR(RDDChild); }
+
+bool RDDChild::Init() {
+ Maybe<FileDescriptor> brokerFd;
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ auto policy = SandboxBrokerPolicyFactory::GetRDDPolicy(OtherPid());
+ if (policy != nullptr) {
+ brokerFd = Some(FileDescriptor());
+ mSandboxBroker =
+ SandboxBroker::Create(std::move(policy), OtherPid(), brokerFd.ref());
+ // This is unlikely to fail and probably indicates OS resource
+ // exhaustion, but we can at least try to recover.
+ if (NS_WARN_IF(mSandboxBroker == nullptr)) {
+ return false;
+ }
+ MOZ_ASSERT(brokerFd.ref().IsValid());
+ }
+#endif // XP_LINUX && MOZ_SANDBOX
+
+ nsTArray<GfxVarUpdate> updates = gfxVars::FetchNonDefaultVars();
+
+ SendInit(updates, brokerFd, Telemetry::CanRecordReleaseData());
+
+#ifdef MOZ_GECKO_PROFILER
+ Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid()));
+#endif
+
+ gfxVars::AddReceiver(this);
+ auto* gpm = gfx::GPUProcessManager::Get();
+ if (gpm) {
+ gpm->AddListener(this);
+ }
+
+ return true;
+}
+
+bool RDDChild::SendRequestMemoryReport(const uint32_t& aGeneration,
+ const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<FileDescriptor>& aDMDFile) {
+ mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration);
+
+ PRDDChild::SendRequestMemoryReport(
+ aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile,
+ [&](const uint32_t& aGeneration2) {
+ if (RDDProcessManager* rddpm = RDDProcessManager::Get()) {
+ if (RDDChild* child = rddpm->GetRDDChild()) {
+ if (child->mMemoryReportRequest) {
+ child->mMemoryReportRequest->Finish(aGeneration2);
+ child->mMemoryReportRequest = nullptr;
+ }
+ }
+ }
+ },
+ [&](mozilla::ipc::ResponseRejectReason) {
+ if (RDDProcessManager* rddpm = RDDProcessManager::Get()) {
+ if (RDDChild* child = rddpm->GetRDDChild()) {
+ child->mMemoryReportRequest = nullptr;
+ }
+ }
+ });
+
+ return true;
+}
+
+void RDDChild::OnCompositorUnexpectedShutdown() {
+ auto* rddm = RDDProcessManager::Get();
+ if (rddm) {
+ rddm->CreateVideoBridge();
+ }
+}
+
+void RDDChild::OnVarChanged(const GfxVarUpdate& aVar) { SendUpdateVar(aVar); }
+
+mozilla::ipc::IPCResult RDDChild::RecvAddMemoryReport(
+ const MemoryReport& aReport) {
+ if (mMemoryReportRequest) {
+ mMemoryReportRequest->RecvReport(aReport);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RDDChild::RecvGetModulesTrust(
+ ModulePaths&& aModPaths, bool aRunAtNormalPriority,
+ GetModulesTrustResolver&& aResolver) {
+#if defined(XP_WIN)
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->GetModulesTrust(std::move(aModPaths), aRunAtNormalPriority)
+ ->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [aResolver](ModulesMapResult&& aResult) {
+ aResolver(Some(ModulesMapResult(std::move(aResult))));
+ },
+ [aResolver](nsresult aRv) { aResolver(Nothing()); });
+ return IPC_OK();
+#else
+ return IPC_FAIL(this, "Unsupported on this platform");
+#endif // defined(XP_WIN)
+}
+
+mozilla::ipc::IPCResult RDDChild::RecvUpdateMediaCodecsSupported(
+ const PDMFactory::MediaCodecsSupported& aSupported) {
+ dom::ContentParent::BroadcastMediaCodecsSupportedUpdate(
+ RemoteDecodeIn::RddProcess, aSupported);
+ return IPC_OK();
+}
+
+void RDDChild::ActorDestroy(ActorDestroyReason aWhy) {
+ if (aWhy == AbnormalShutdown) {
+ GenerateCrashReport(OtherPid());
+ }
+
+ auto* gpm = gfx::GPUProcessManager::Get();
+ if (gpm) {
+ // Note: the manager could have shutdown already.
+ gpm->RemoveListener(this);
+ }
+
+ gfxVars::RemoveReceiver(this);
+ mHost->OnChannelClosed();
+}
+
+class DeferredDeleteRDDChild : public Runnable {
+ public:
+ explicit DeferredDeleteRDDChild(UniquePtr<RDDChild>&& aChild)
+ : Runnable("gfx::DeferredDeleteRDDChild"), mChild(std::move(aChild)) {}
+
+ NS_IMETHODIMP Run() override { return NS_OK; }
+
+ private:
+ UniquePtr<RDDChild> mChild;
+};
+
+/* static */
+void RDDChild::Destroy(UniquePtr<RDDChild>&& aChild) {
+ NS_DispatchToMainThread(new DeferredDeleteRDDChild(std::move(aChild)));
+}
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RDDChild.h b/dom/media/ipc/RDDChild.h
new file mode 100644
index 0000000000..af90afe11e
--- /dev/null
+++ b/dom/media/ipc/RDDChild.h
@@ -0,0 +1,67 @@
+/* -*- 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/. */
+#ifndef _include_dom_media_ipc_RDDChild_h_
+#define _include_dom_media_ipc_RDDChild_h_
+#include "mozilla/PRDDChild.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/gfx/GPUProcessListener.h"
+#include "mozilla/gfx/gfxVarReceiver.h"
+#include "mozilla/ipc/CrashReporterHelper.h"
+
+namespace mozilla {
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+class SandboxBroker;
+#endif
+
+namespace dom {
+class MemoryReportRequestHost;
+} // namespace dom
+
+class RDDProcessHost;
+
+class RDDChild final : public PRDDChild,
+ public ipc::CrashReporterHelper<GeckoProcessType_RDD>,
+ public gfx::gfxVarReceiver,
+ public gfx::GPUProcessListener {
+ typedef mozilla::dom::MemoryReportRequestHost MemoryReportRequestHost;
+
+ public:
+ explicit RDDChild(RDDProcessHost* aHost);
+ ~RDDChild();
+
+ bool Init();
+
+ void OnCompositorUnexpectedShutdown() override;
+ void OnVarChanged(const GfxVarUpdate& aVar) override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport);
+ mozilla::ipc::IPCResult RecvGetModulesTrust(
+ ModulePaths&& aModPaths, bool aRunAtNormalPriority,
+ GetModulesTrustResolver&& aResolver);
+ mozilla::ipc::IPCResult RecvUpdateMediaCodecsSupported(
+ const PDMFactory::MediaCodecsSupported& aSupported);
+
+ bool SendRequestMemoryReport(const uint32_t& aGeneration,
+ const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<ipc::FileDescriptor>& aDMDFile);
+
+ static void Destroy(UniquePtr<RDDChild>&& aChild);
+
+ private:
+ RDDProcessHost* mHost;
+ UniquePtr<MemoryReportRequestHost> mMemoryReportRequest;
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ UniquePtr<SandboxBroker> mSandboxBroker;
+#endif
+};
+
+} // namespace mozilla
+
+#endif // _include_dom_media_ipc_RDDChild_h_
diff --git a/dom/media/ipc/RDDParent.cpp b/dom/media/ipc/RDDParent.cpp
new file mode 100644
index 0000000000..9bbd5ae18c
--- /dev/null
+++ b/dom/media/ipc/RDDParent.cpp
@@ -0,0 +1,304 @@
+/* -*- 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 "RDDParent.h"
+
+#if defined(XP_WIN)
+# include <dwrite.h>
+# include <process.h>
+
+# include "WMF.h"
+# include "WMFDecoderModule.h"
+# include "mozilla/WinDllServices.h"
+# include "mozilla/gfx/DeviceManagerDx.h"
+#else
+# include <unistd.h>
+#endif
+
+#include "PDMFactory.h"
+#include "chrome/common/ipc_channel.h"
+#include "gfxConfig.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/HangDetails.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/RemoteDecoderManagerChild.h"
+#include "mozilla/RemoteDecoderManagerParent.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/dom/MemoryReportRequest.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/ipc/CrashReporterClient.h"
+#include "mozilla/ipc/ProcessChild.h"
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+# include "mozilla/Sandbox.h"
+#endif
+
+#ifdef MOZ_GECKO_PROFILER
+# include "ChildProfilerController.h"
+#endif
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+# include "RDDProcessHost.h"
+# include "mozilla/Sandbox.h"
+# include "nsMacUtilsImpl.h"
+#endif
+
+#include "ProcessUtils.h"
+#include "nsDebugImpl.h"
+#include "nsThreadManager.h"
+
+namespace mozilla {
+
+using namespace ipc;
+using namespace gfx;
+
+static RDDParent* sRDDParent;
+
+RDDParent::RDDParent() : mLaunchTime(TimeStamp::Now()) { sRDDParent = this; }
+
+RDDParent::~RDDParent() { sRDDParent = nullptr; }
+
+/* static */
+RDDParent* RDDParent::GetSingleton() {
+ MOZ_DIAGNOSTIC_ASSERT(sRDDParent);
+ return sRDDParent;
+}
+
+bool RDDParent::Init(base::ProcessId aParentPid, const char* aParentBuildID,
+ MessageLoop* aIOLoop, UniquePtr<IPC::Channel> aChannel) {
+ // Initialize the thread manager before starting IPC. Otherwise, messages
+ // may be posted to the main thread and we won't be able to process them.
+ if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) {
+ return false;
+ }
+
+ // Now it's safe to start IPC.
+ if (NS_WARN_IF(!Open(std::move(aChannel), aParentPid, aIOLoop))) {
+ return false;
+ }
+
+ nsDebugImpl::SetMultiprocessMode("RDD");
+
+ // This must be checked before any IPDL message, which may hit sentinel
+ // errors due to parent and content processes having different
+ // versions.
+ MessageChannel* channel = GetIPCChannel();
+ if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID)) {
+ // We need to quit this process if the buildID doesn't match the parent's.
+ // This can occur when an update occurred in the background.
+ ProcessChild::QuickExit();
+ }
+
+ // Init crash reporter support.
+ CrashReporterClient::InitSingleton(this);
+
+ if (NS_FAILED(NS_InitMinimalXPCOM())) {
+ return false;
+ }
+
+ gfxConfig::Init();
+ gfxVars::Initialize();
+#ifdef XP_WIN
+ DeviceManagerDx::Init();
+ wmf::MFStartup();
+#endif
+
+ mozilla::ipc::SetThisProcessName("RDD Process");
+
+ return true;
+}
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+extern "C" {
+void CGSShutdownServerConnections();
+};
+#endif
+
+mozilla::ipc::IPCResult RDDParent::RecvInit(
+ nsTArray<GfxVarUpdate>&& vars, const Maybe<FileDescriptor>& aBrokerFd,
+ const bool& aCanRecordReleaseTelemetry) {
+ for (const auto& var : vars) {
+ gfxVars::ApplyUpdate(var);
+ }
+
+ auto supported = PDMFactory::Supported();
+ Unused << SendUpdateMediaCodecsSupported(supported);
+
+#if defined(MOZ_SANDBOX)
+# if defined(XP_MACOSX)
+ // Close all current connections to the WindowServer. This ensures that the
+ // Activity Monitor will not label the content process as "Not responding"
+ // because it's not running a native event loop. See bug 1384336.
+ CGSShutdownServerConnections();
+
+# elif defined(XP_LINUX)
+ int fd = -1;
+ if (aBrokerFd.isSome()) {
+ fd = aBrokerFd.value().ClonePlatformHandle().release();
+ }
+ SetRemoteDataDecoderSandbox(fd);
+# endif // XP_MACOSX/XP_LINUX
+#endif // MOZ_SANDBOX
+
+#if defined(XP_WIN)
+ if (aCanRecordReleaseTelemetry) {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->StartUntrustedModulesProcessor();
+ }
+#endif // defined(XP_WIN)
+ return IPC_OK();
+}
+
+IPCResult RDDParent::RecvUpdateVar(const GfxVarUpdate& aUpdate) {
+#if defined(XP_WIN)
+ auto scopeExit = MakeScopeExit(
+ [couldUseHWDecoder = gfx::gfxVars::CanUseHardwareVideoDecoding()] {
+ if (couldUseHWDecoder != gfx::gfxVars::CanUseHardwareVideoDecoding()) {
+ // The capabilities of the system may have changed, force a refresh by
+ // re-initializing the WMF PDM.
+ WMFDecoderModule::Init();
+ Unused << RDDParent::GetSingleton()->SendUpdateMediaCodecsSupported(
+ PDMFactory::Supported(true /* force refresh */));
+ }
+ });
+#endif
+ gfxVars::ApplyUpdate(aUpdate);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RDDParent::RecvInitProfiler(
+ Endpoint<PProfilerChild>&& aEndpoint) {
+#ifdef MOZ_GECKO_PROFILER
+ mProfilerController = ChildProfilerController::Create(std::move(aEndpoint));
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RDDParent::RecvNewContentRemoteDecoderManager(
+ Endpoint<PRemoteDecoderManagerParent>&& aEndpoint) {
+ if (!RemoteDecoderManagerParent::CreateForContent(std::move(aEndpoint))) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RDDParent::RecvInitVideoBridge(
+ Endpoint<PVideoBridgeChild>&& aEndpoint, const bool& aCreateHardwareDevice,
+ const ContentDeviceData& aContentDeviceData) {
+ if (!RemoteDecoderManagerParent::CreateVideoBridgeToOtherProcess(
+ std::move(aEndpoint))) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ gfxConfig::Inherit(
+ {
+ Feature::HW_COMPOSITING,
+ Feature::D3D11_COMPOSITING,
+ Feature::OPENGL_COMPOSITING,
+ Feature::ADVANCED_LAYERS,
+ Feature::DIRECT2D,
+ Feature::WEBGPU,
+ },
+ aContentDeviceData.prefs());
+#ifdef XP_WIN
+ if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+ auto* devmgr = DeviceManagerDx::Get();
+ if (devmgr) {
+ devmgr->ImportDeviceInfo(aContentDeviceData.d3d11());
+ if (aCreateHardwareDevice) {
+ devmgr->CreateContentDevices();
+ }
+ }
+ }
+#endif
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RDDParent::RecvRequestMemoryReport(
+ const uint32_t& aGeneration, const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile,
+ const RequestMemoryReportResolver& aResolver) {
+ nsPrintfCString processName("RDD (pid %u)", (unsigned)getpid());
+
+ mozilla::dom::MemoryReportRequestClient::Start(
+ aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, processName,
+ [&](const MemoryReport& aReport) {
+ Unused << GetSingleton()->SendAddMemoryReport(aReport);
+ },
+ aResolver);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RDDParent::RecvGetUntrustedModulesData(
+ GetUntrustedModulesDataResolver&& aResolver) {
+#if defined(XP_WIN)
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->GetUntrustedModulesData()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [aResolver](Maybe<UntrustedModulesData>&& aData) {
+ aResolver(std::move(aData));
+ },
+ [aResolver](nsresult aReason) { aResolver(Nothing()); });
+ return IPC_OK();
+#else
+ return IPC_FAIL(this, "Unsupported on this platform");
+#endif // defined(XP_WIN)
+}
+
+mozilla::ipc::IPCResult RDDParent::RecvPreferenceUpdate(const Pref& aPref) {
+ Preferences::SetPreference(aPref);
+ return IPC_OK();
+}
+
+void RDDParent::ActorDestroy(ActorDestroyReason aWhy) {
+ if (AbnormalShutdown == aWhy) {
+ NS_WARNING("Shutting down RDD process early due to a crash!");
+ ProcessChild::QuickExit();
+ }
+
+#ifndef NS_FREE_PERMANENT_DATA
+# ifdef XP_WIN
+ wmf::MFShutdown();
+# endif
+ // No point in going through XPCOM shutdown because we don't keep persistent
+ // state.
+ ProcessChild::QuickExit();
+#endif
+
+ // Wait until all RemoteDecoderManagerParent have closed.
+ mShutdownBlockers.WaitUntilClear(10 * 1000 /* 10s timeout*/)
+ ->Then(GetCurrentSerialEventTarget(), __func__, [this]() {
+
+#ifdef XP_WIN
+ wmf::MFShutdown();
+#endif
+
+#if defined(XP_WIN)
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->DisableFull();
+#endif // defined(XP_WIN)
+
+#ifdef MOZ_GECKO_PROFILER
+ if (mProfilerController) {
+ mProfilerController->Shutdown();
+ mProfilerController = nullptr;
+ }
+#endif
+
+ RemoteDecoderManagerParent::ShutdownVideoBridge();
+
+#ifdef XP_WIN
+ DeviceManagerDx::Shutdown();
+#endif
+ gfxVars::Shutdown();
+ gfxConfig::Shutdown();
+ CrashReporterClient::DestroySingleton();
+ XRE_ShutdownChildProcess();
+ });
+}
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RDDParent.h b/dom/media/ipc/RDDParent.h
new file mode 100644
index 0000000000..8f0e5ea737
--- /dev/null
+++ b/dom/media/ipc/RDDParent.h
@@ -0,0 +1,64 @@
+/* -*- 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/. */
+#ifndef _include_dom_media_ipc_RDDParent_h__
+#define _include_dom_media_ipc_RDDParent_h__
+#include "mozilla/PRDDParent.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/media/MediaUtils.h"
+
+namespace mozilla {
+
+class TimeStamp;
+class ChildProfilerController;
+
+class RDDParent final : public PRDDParent {
+ public:
+ RDDParent();
+ ~RDDParent();
+
+ static RDDParent* GetSingleton();
+
+ AsyncBlockers& AsyncShutdownService() { return mShutdownBlockers; }
+
+ bool Init(base::ProcessId aParentPid, const char* aParentBuildID,
+ MessageLoop* aIOLoop, UniquePtr<IPC::Channel> aChannel);
+
+ mozilla::ipc::IPCResult RecvInit(nsTArray<GfxVarUpdate>&& vars,
+ const Maybe<ipc::FileDescriptor>& aBrokerFd,
+ const bool& aCanRecordReleaseTelemetry);
+ mozilla::ipc::IPCResult RecvInitProfiler(
+ Endpoint<PProfilerChild>&& aEndpoint);
+
+ mozilla::ipc::IPCResult RecvNewContentRemoteDecoderManager(
+ Endpoint<PRemoteDecoderManagerParent>&& aEndpoint);
+ mozilla::ipc::IPCResult RecvInitVideoBridge(
+ Endpoint<PVideoBridgeChild>&& aEndpoint,
+ const bool& aCreateHardwareDevice,
+ const ContentDeviceData& aContentDeviceData);
+ mozilla::ipc::IPCResult RecvRequestMemoryReport(
+ const uint32_t& generation, const bool& anonymize,
+ const bool& minimizeMemoryUsage,
+ const Maybe<ipc::FileDescriptor>& DMDFile,
+ const RequestMemoryReportResolver& aResolver);
+ mozilla::ipc::IPCResult RecvGetUntrustedModulesData(
+ GetUntrustedModulesDataResolver&& aResolver);
+ mozilla::ipc::IPCResult RecvPreferenceUpdate(const Pref& pref);
+ mozilla::ipc::IPCResult RecvUpdateVar(const GfxVarUpdate& pref);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ private:
+ const TimeStamp mLaunchTime;
+#ifdef MOZ_GECKO_PROFILER
+ RefPtr<ChildProfilerController> mProfilerController;
+#endif
+ AsyncBlockers mShutdownBlockers;
+};
+
+} // namespace mozilla
+
+#endif // _include_dom_media_ipc_RDDParent_h__
diff --git a/dom/media/ipc/RDDProcessHost.cpp b/dom/media/ipc/RDDProcessHost.cpp
new file mode 100644
index 0000000000..c17c0d292f
--- /dev/null
+++ b/dom/media/ipc/RDDProcessHost.cpp
@@ -0,0 +1,311 @@
+/* -*- 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 "RDDProcessHost.h"
+
+#include "ProcessUtils.h"
+#include "RDDChild.h"
+#include "chrome/common/process_watcher.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_media.h"
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+# include "mozilla/Sandbox.h"
+#endif
+
+namespace mozilla {
+
+using namespace ipc;
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+bool RDDProcessHost::sLaunchWithMacSandbox = false;
+#endif
+
+RDDProcessHost::RDDProcessHost(Listener* aListener)
+ : GeckoChildProcessHost(GeckoProcessType_RDD),
+ mListener(aListener),
+ mLiveToken(new media::Refcountable<bool>(true)) {
+ MOZ_COUNT_CTOR(RDDProcessHost);
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ if (!sLaunchWithMacSandbox) {
+ sLaunchWithMacSandbox = (PR_GetEnv("MOZ_DISABLE_RDD_SANDBOX") == nullptr);
+ }
+ mDisableOSActivityMode = sLaunchWithMacSandbox;
+#endif
+}
+
+RDDProcessHost::~RDDProcessHost() { MOZ_COUNT_DTOR(RDDProcessHost); }
+
+bool RDDProcessHost::Launch(StringVector aExtraOpts) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
+ MOZ_ASSERT(!mRDDChild);
+
+ mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>();
+ if (!mPrefSerializer->SerializeToSharedMemory()) {
+ return false;
+ }
+ mPrefSerializer->AddSharedPrefCmdLineArgs(*this, aExtraOpts);
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ mSandboxLevel = Preferences::GetInt("security.sandbox.rdd.level");
+#endif
+
+ mLaunchPhase = LaunchPhase::Waiting;
+ mLaunchTime = TimeStamp::Now();
+
+ int32_t timeoutMs = StaticPrefs::media_rdd_process_startup_timeout_ms();
+
+ // If one of the following environment variables are set we can
+ // effectively ignore the timeout - as we can guarantee the RDD
+ // process will be terminated
+ if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS") ||
+ PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) {
+ timeoutMs = 0;
+ }
+ if (timeoutMs) {
+ // We queue a delayed task. If that task runs before the
+ // WhenProcessHandleReady promise gets resolved, we will abort the launch.
+ GetMainThreadSerialEventTarget()->DelayedDispatch(
+ NS_NewRunnableFunction(
+ "RDDProcessHost::Launchtimeout",
+ [this, liveToken = mLiveToken]() {
+ if (!*liveToken || mTimerChecked) {
+ // We have been deleted or the runnable has already started, we
+ // can abort.
+ return;
+ }
+ InitAfterConnect(false);
+ MOZ_ASSERT(mTimerChecked,
+ "InitAfterConnect must have acted on the promise");
+ }),
+ timeoutMs);
+ }
+
+ if (!GeckoChildProcessHost::AsyncLaunch(aExtraOpts)) {
+ mLaunchPhase = LaunchPhase::Complete;
+ mPrefSerializer = nullptr;
+ return false;
+ }
+ return true;
+}
+
+RefPtr<GenericNonExclusivePromise> RDDProcessHost::LaunchPromise() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mLaunchPromise) {
+ return mLaunchPromise;
+ }
+ mLaunchPromise = MakeRefPtr<GenericNonExclusivePromise::Private>(__func__);
+ WhenProcessHandleReady()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [this, liveToken = mLiveToken](
+ const ipc::ProcessHandlePromise::ResolveOrRejectValue& aResult) {
+ if (!*liveToken) {
+ // The RDDProcessHost got deleted. Abort. The promise would have
+ // already been rejected.
+ return;
+ }
+ if (mTimerChecked) {
+ // We hit the timeout earlier, abort.
+ return;
+ }
+ mTimerChecked = true;
+ if (aResult.IsReject()) {
+ RejectPromise();
+ }
+ // If aResult.IsResolve() then we have succeeded in launching the
+ // RDD process. The promise will be resolved once the channel has
+ // connected (or failed to) later.
+ });
+ return mLaunchPromise;
+}
+
+void RDDProcessHost::OnChannelConnected(int32_t peer_pid) {
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ GeckoChildProcessHost::OnChannelConnected(peer_pid);
+
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "RDDProcessHost::OnChannelConnected", [this, liveToken = mLiveToken]() {
+ if (*liveToken && mLaunchPhase == LaunchPhase::Waiting) {
+ InitAfterConnect(true);
+ }
+ }));
+}
+
+void RDDProcessHost::OnChannelError() {
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ GeckoChildProcessHost::OnChannelError();
+
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "RDDProcessHost::OnChannelError", [this, liveToken = mLiveToken]() {
+ if (*liveToken && mLaunchPhase == LaunchPhase::Waiting) {
+ InitAfterConnect(false);
+ }
+ }));
+}
+
+static uint64_t sRDDProcessTokenCounter = 0;
+
+void RDDProcessHost::InitAfterConnect(bool aSucceeded) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
+ MOZ_ASSERT(!mRDDChild);
+
+ mLaunchPhase = LaunchPhase::Complete;
+
+ if (!aSucceeded) {
+ RejectPromise();
+ return;
+ }
+ mProcessToken = ++sRDDProcessTokenCounter;
+ mRDDChild = MakeUnique<RDDChild>(this);
+ DebugOnly<bool> rv =
+ mRDDChild->Open(TakeChannel(), base::GetProcId(GetChildProcessHandle()));
+ MOZ_ASSERT(rv);
+
+ // Only clear mPrefSerializer in the success case to avoid a
+ // possible race in the case case of a timeout on Windows launch.
+ // See Bug 1555076 comment 7:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1555076#c7
+ mPrefSerializer = nullptr;
+
+ if (!mRDDChild->Init()) {
+ // Can't just kill here because it will create a timing race that
+ // will crash the tab. We don't really want to crash the tab just
+ // because RDD linux sandbox failed to initialize. In this case,
+ // we'll close the child channel which will cause the RDD process
+ // to shutdown nicely avoiding the tab crash (which manifests as
+ // Bug 1535335).
+ mRDDChild->Close();
+ RejectPromise();
+ } else {
+ ResolvePromise();
+ }
+}
+
+void RDDProcessHost::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mShutdownRequested);
+
+ RejectPromise();
+
+ if (mRDDChild) {
+ // OnChannelClosed uses this to check if the shutdown was expected or
+ // unexpected.
+ mShutdownRequested = true;
+
+ // The channel might already be closed if we got here unexpectedly.
+ if (!mChannelClosed) {
+ mRDDChild->Close();
+ }
+
+#ifndef NS_FREE_PERMANENT_DATA
+ // No need to communicate shutdown, the RDD process doesn't need to
+ // communicate anything back.
+ KillHard("NormalShutdown");
+#endif
+
+ // If we're shutting down unexpectedly, we're in the middle of handling an
+ // ActorDestroy for PRDDChild, which is still on the stack. We'll return
+ // back to OnChannelClosed.
+ //
+ // Otherwise, we'll wait for OnChannelClose to be called whenever PRDDChild
+ // acknowledges shutdown.
+ return;
+ }
+
+ DestroyProcess();
+}
+
+void RDDProcessHost::OnChannelClosed() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mChannelClosed = true;
+ RejectPromise();
+
+ if (!mShutdownRequested && mListener) {
+ // This is an unclean shutdown. Notify our listener that we're going away.
+ mListener->OnProcessUnexpectedShutdown(this);
+ } else {
+ DestroyProcess();
+ }
+
+ // Release the actor.
+ RDDChild::Destroy(std::move(mRDDChild));
+}
+
+void RDDProcessHost::KillHard(const char* aReason) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ ProcessHandle handle = GetChildProcessHandle();
+ if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER, false)) {
+ NS_WARNING("failed to kill subprocess!");
+ }
+
+ SetAlreadyDead();
+}
+
+uint64_t RDDProcessHost::GetProcessToken() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mProcessToken;
+}
+
+void RDDProcessHost::DestroyProcess() {
+ MOZ_ASSERT(NS_IsMainThread());
+ RejectPromise();
+
+ // Any pending tasks will be cancelled from now on.
+ *mLiveToken = false;
+
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction("DestroyProcessRunnable", [this] { Destroy(); }));
+}
+
+void RDDProcessHost::ResolvePromise() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mLaunchPromiseSettled) {
+ mLaunchPromise->Resolve(true, __func__);
+ mLaunchPromiseSettled = true;
+ }
+ // We have already acted on the promise; the timeout runnable no longer needs
+ // to interrupt anything.
+ mTimerChecked = true;
+}
+
+void RDDProcessHost::RejectPromise() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mLaunchPromiseSettled) {
+ mLaunchPromise->Reject(NS_ERROR_FAILURE, __func__);
+ mLaunchPromiseSettled = true;
+ }
+ // We have already acted on the promise; the timeout runnable no longer needs
+ // to interrupt anything.
+ mTimerChecked = true;
+}
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+bool RDDProcessHost::FillMacSandboxInfo(MacSandboxInfo& aInfo) {
+ GeckoChildProcessHost::FillMacSandboxInfo(aInfo);
+ if (!aInfo.shouldLog && PR_GetEnv("MOZ_SANDBOX_RDD_LOGGING")) {
+ aInfo.shouldLog = true;
+ }
+ return true;
+}
+
+/* static */
+MacSandboxType RDDProcessHost::GetMacSandboxType() {
+ return MacSandboxType_RDD;
+}
+#endif
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RDDProcessHost.h b/dom/media/ipc/RDDProcessHost.h
new file mode 100644
index 0000000000..199c414024
--- /dev/null
+++ b/dom/media/ipc/RDDProcessHost.h
@@ -0,0 +1,163 @@
+/* -*- 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/. */
+
+#ifndef _include_dom_media_ipc_RDDProcessHost_h_
+#define _include_dom_media_ipc_RDDProcessHost_h_
+#include "mozilla/UniquePtr.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/media/MediaUtils.h"
+
+namespace mozilla {
+namespace ipc {
+class SharedPreferenceSerializer;
+}
+} // namespace mozilla
+class nsITimer;
+
+namespace mozilla {
+
+class RDDChild;
+
+// RDDProcessHost is the "parent process" container for a subprocess handle and
+// IPC connection. It owns the parent process IPDL actor, which in this case,
+// is a RDDChild.
+//
+// RDDProcessHosts are allocated and managed by RDDProcessManager. For all
+// intents and purposes it is a singleton, though more than one may be allocated
+// at a time due to its shutdown being asynchronous.
+class RDDProcessHost final : public mozilla::ipc::GeckoChildProcessHost {
+ friend class RDDChild;
+
+ public:
+ class Listener {
+ public:
+ // The RDDProcessHost has unexpectedly shutdown or had its connection
+ // severed. This is not called if an error occurs after calling
+ // Shutdown().
+ virtual void OnProcessUnexpectedShutdown(RDDProcessHost* aHost) {}
+ };
+
+ explicit RDDProcessHost(Listener* listener);
+
+ // Launch the subprocess asynchronously. On failure, false is returned.
+ // Otherwise, true is returned. If succeeded, a follow-up call should be made
+ // to LaunchPromise() which will return a promise that will be resolved once
+ // the RDD process has launched and a channel has been established.
+ //
+ // @param aExtraOpts (StringVector)
+ // Extra options to pass to the subprocess.
+ bool Launch(StringVector aExtraOpts);
+
+ // Return a promise that will be resolved once the process has completed its
+ // launch. The promise will be immediately resolved if the launch has already
+ // succeeded.
+ RefPtr<GenericNonExclusivePromise> LaunchPromise();
+
+ // Inform the process that it should clean up its resources and shut
+ // down. This initiates an asynchronous shutdown sequence. After this
+ // method returns, it is safe for the caller to forget its pointer to
+ // the RDDProcessHost.
+ //
+ // After this returns, the attached Listener is no longer used.
+ void Shutdown();
+
+ // Return the actor for the top-level actor of the process. If the process
+ // has not connected yet, this returns null.
+ RDDChild* GetActor() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mRDDChild.get();
+ }
+
+ // Return a unique id for this process, guaranteed not to be shared with any
+ // past or future instance of RDDProcessHost.
+ uint64_t GetProcessToken() const;
+
+ bool IsConnected() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ return !!mRDDChild;
+ }
+
+ // Return the time stamp for when we tried to launch the RDD process.
+ // This is currently used for Telemetry so that we can determine how
+ // long RDD processes take to spin up. Note this doesn't denote a
+ // successful launch, just when we attempted launch.
+ TimeStamp GetLaunchTime() const { return mLaunchTime; }
+
+ // Called on the IO thread.
+ void OnChannelConnected(int32_t peer_pid) override;
+ void OnChannelError() override;
+
+ void SetListener(Listener* aListener);
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ // Return the sandbox type to be used with this process type.
+ static MacSandboxType GetMacSandboxType();
+#endif
+
+ private:
+ ~RDDProcessHost();
+
+ // Called on the main thread with true after a connection has been established
+ // or false if it failed (including if it failed before the timeout kicked in)
+ void InitAfterConnect(bool aSucceeded);
+
+ // Called on the main thread when the mRDDChild actor is shutting down.
+ void OnChannelClosed();
+
+ // Kill the remote process, triggering IPC shutdown.
+ void KillHard(const char* aReason);
+
+ void DestroyProcess();
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ static bool sLaunchWithMacSandbox;
+
+ // Sandbox the RDD process at launch for all instances
+ bool IsMacSandboxLaunchEnabled() override { return sLaunchWithMacSandbox; }
+
+ // Override so we can turn on RDD process-specific sandbox logging
+ bool FillMacSandboxInfo(MacSandboxInfo& aInfo) override;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(RDDProcessHost);
+
+ Listener* const mListener;
+
+ // All members below are only ever accessed on the main thread.
+ enum class LaunchPhase { Unlaunched, Waiting, Complete };
+ LaunchPhase mLaunchPhase = LaunchPhase::Unlaunched;
+
+ UniquePtr<RDDChild> mRDDChild;
+ uint64_t mProcessToken = 0;
+
+ UniquePtr<ipc::SharedPreferenceSerializer> mPrefSerializer;
+
+ bool mShutdownRequested = false;
+ bool mChannelClosed = false;
+
+ TimeStamp mLaunchTime;
+ void RejectPromise();
+ void ResolvePromise();
+
+ // Set to true on construction and to false just prior deletion.
+ // The RDDProcessHost isn't refcounted; so we can capture this by value in
+ // lambdas along with a strong reference to mLiveToken and check if that value
+ // is true before accessing "this".
+ // While a reference to mLiveToken can be taken on any thread; its value can
+ // only be read on the main thread.
+ const RefPtr<media::Refcountable<bool>> mLiveToken;
+ RefPtr<GenericNonExclusivePromise::Private> mLaunchPromise;
+ bool mLaunchPromiseSettled = false;
+ // Will be set to true if we've exceeded the allowed startup time or if the
+ // RDD process as successfully started. This is used to determine if the
+ // timeout runnable needs to execute code or not.
+ bool mTimerChecked = false;
+};
+
+} // namespace mozilla
+
+#endif // _include_dom_media_ipc_RDDProcessHost_h_
diff --git a/dom/media/ipc/RDDProcessImpl.cpp b/dom/media/ipc/RDDProcessImpl.cpp
new file mode 100644
index 0000000000..facf0b148a
--- /dev/null
+++ b/dom/media/ipc/RDDProcessImpl.cpp
@@ -0,0 +1,82 @@
+/* -*- 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 "RDDProcessImpl.h"
+
+#include "mozilla/ipc/IOThreadChild.h"
+
+#if defined(OS_WIN) && defined(MOZ_SANDBOX)
+# include "mozilla/sandboxTarget.h"
+#endif
+
+#include "ProcessUtils.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+RDDProcessImpl::RDDProcessImpl(ProcessId aParentPid)
+ : ProcessChild(aParentPid) {}
+
+RDDProcessImpl::~RDDProcessImpl() = default;
+
+bool RDDProcessImpl::Init(int aArgc, char* aArgv[]) {
+#if defined(MOZ_SANDBOX) && defined(OS_WIN)
+ // Preload AV dlls so we can enable Binary Signature Policy
+ // to restrict further dll loads.
+ LoadLibraryW(L"mozavcodec.dll");
+ LoadLibraryW(L"mozavutil.dll");
+ mozilla::SandboxTarget::Instance()->StartSandbox();
+#endif
+ char* parentBuildID = nullptr;
+ char* prefsHandle = nullptr;
+ char* prefMapHandle = nullptr;
+ char* prefsLen = nullptr;
+ char* prefMapSize = nullptr;
+ for (int i = 1; i < aArgc; i++) {
+ if (!aArgv[i]) {
+ continue;
+ }
+ if (strcmp(aArgv[i], "-parentBuildID") == 0) {
+ parentBuildID = aArgv[i + 1];
+
+#ifdef XP_WIN
+ } else if (strcmp(aArgv[i], "-prefsHandle") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefsHandle = aArgv[i];
+ } else if (strcmp(aArgv[i], "-prefMapHandle") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefMapHandle = aArgv[i];
+#endif
+ } else if (strcmp(aArgv[i], "-prefsLen") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefsLen = aArgv[i];
+ } else if (strcmp(aArgv[i], "-prefMapSize") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefMapSize = aArgv[i];
+ }
+ }
+
+ SharedPreferenceDeserializer deserializer;
+ if (!deserializer.DeserializeFromSharedMemory(prefsHandle, prefMapHandle,
+ prefsLen, prefMapSize)) {
+ return false;
+ }
+
+ return mRDD.Init(ParentPid(), parentBuildID, IOThreadChild::message_loop(),
+ IOThreadChild::TakeChannel());
+}
+
+void RDDProcessImpl::CleanUp() { NS_ShutdownXPCOM(nullptr); }
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RDDProcessImpl.h b/dom/media/ipc/RDDProcessImpl.h
new file mode 100644
index 0000000000..7406b32293
--- /dev/null
+++ b/dom/media/ipc/RDDProcessImpl.h
@@ -0,0 +1,41 @@
+/* -*- 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/. */
+#ifndef _include_dom_media_ipc_RDDProcessImpl_h__
+#define _include_dom_media_ipc_RDDProcessImpl_h__
+#include "mozilla/ipc/ProcessChild.h"
+
+#if defined(XP_WIN)
+# include "mozilla/mscom/ProcessRuntime.h"
+#endif
+
+#include "RDDParent.h"
+
+namespace mozilla {
+
+// This class owns the subprocess instance of a PRDD - which in this case,
+// is a RDDParent. It is instantiated as a singleton in XRE_InitChildProcess.
+class RDDProcessImpl final : public ipc::ProcessChild {
+ public:
+ explicit RDDProcessImpl(ProcessId aParentPid);
+ ~RDDProcessImpl();
+
+ bool Init(int aArgc, char* aArgv[]) override;
+ void CleanUp() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RDDProcessImpl);
+
+ RDDParent mRDD;
+
+#if defined(XP_WIN)
+ // This object initializes and configures COM.
+ mozilla::mscom::ProcessRuntime mCOMRuntime;
+#endif
+};
+
+} // namespace mozilla
+
+#endif // _include_dom_media_ipc_RDDProcessImpl_h__
diff --git a/dom/media/ipc/RDDProcessManager.cpp b/dom/media/ipc/RDDProcessManager.cpp
new file mode 100644
index 0000000000..b9aa7c06ad
--- /dev/null
+++ b/dom/media/ipc/RDDProcessManager.cpp
@@ -0,0 +1,373 @@
+/* -*- 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 "RDDProcessManager.h"
+
+#include "RDDChild.h"
+#include "RDDProcessHost.h"
+#include "mozilla/MemoryReportingProcess.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/RemoteDecoderManagerChild.h"
+#include "mozilla/RemoteDecoderManagerParent.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/SyncRunnable.h" // for LaunchRDDProcess
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/VideoBridgeParent.h"
+#include "nsAppRunner.h"
+#include "nsContentUtils.h"
+
+namespace mozilla {
+
+using namespace gfx;
+using namespace layers;
+
+static StaticAutoPtr<RDDProcessManager> sRDDSingleton;
+
+RDDProcessManager* RDDProcessManager::Get() { return sRDDSingleton; }
+
+void RDDProcessManager::Initialize() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ sRDDSingleton = new RDDProcessManager();
+}
+
+void RDDProcessManager::Shutdown() { sRDDSingleton = nullptr; }
+
+RDDProcessManager::RDDProcessManager()
+ : mObserver(new Observer(this)), mTaskFactory(this) {
+ MOZ_COUNT_CTOR(RDDProcessManager);
+ // Start listening for pref changes so we can
+ // forward them to the process once it is running.
+ nsContentUtils::RegisterShutdownObserver(mObserver);
+ Preferences::AddStrongObserver(mObserver, "");
+}
+
+RDDProcessManager::~RDDProcessManager() {
+ MOZ_COUNT_DTOR(RDDProcessManager);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // The RDD process should have already been shut down.
+ MOZ_ASSERT(!mProcess && !mRDDChild);
+}
+
+NS_IMPL_ISUPPORTS(RDDProcessManager::Observer, nsIObserver);
+
+RDDProcessManager::Observer::Observer(RDDProcessManager* aManager)
+ : mManager(aManager) {}
+
+NS_IMETHODIMP
+RDDProcessManager::Observer::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+ mManager->OnXPCOMShutdown();
+ } else if (!strcmp(aTopic, "nsPref:changed")) {
+ mManager->OnPreferenceChange(aData);
+ }
+ return NS_OK;
+}
+
+void RDDProcessManager::OnXPCOMShutdown() {
+ nsContentUtils::UnregisterShutdownObserver(mObserver);
+ Preferences::RemoveObserver(mObserver, "");
+ CleanShutdown();
+}
+
+void RDDProcessManager::OnPreferenceChange(const char16_t* aData) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!mProcess) {
+ // Process hasn't been launched yet
+ return;
+ }
+ // A pref changed. If it is useful to do so, inform child processes.
+ if (!dom::ContentParent::ShouldSyncPreference(aData)) {
+ return;
+ }
+
+ // We know prefs are ASCII here.
+ NS_LossyConvertUTF16toASCII strData(aData);
+
+ mozilla::dom::Pref pref(strData, /* isLocked */ false, Nothing(), Nothing());
+ Preferences::GetPreference(&pref);
+ if (!!mRDDChild) {
+ MOZ_ASSERT(mQueuedPrefs.IsEmpty());
+ mRDDChild->SendPreferenceUpdate(pref);
+ } else if (IsRDDProcessLaunching()) {
+ mQueuedPrefs.AppendElement(pref);
+ }
+}
+
+RefPtr<GenericNonExclusivePromise> RDDProcessManager::LaunchRDDProcess() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!Get()) {
+ // Shutdown?
+ return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
+ __func__);
+ }
+
+ if (mNumProcessAttempts && !StaticPrefs::media_rdd_retryonfailure_enabled()) {
+ // We failed to start the RDD process earlier, abort now.
+ return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
+ __func__);
+ }
+
+ if (mLaunchRDDPromise && mProcess) {
+ return mLaunchRDDPromise;
+ }
+
+ std::vector<std::string> extraArgs;
+ nsCString parentBuildID(mozilla::PlatformBuildID());
+ extraArgs.push_back("-parentBuildID");
+ extraArgs.push_back(parentBuildID.get());
+
+ // The subprocess is launched asynchronously, so we
+ // wait for the promise to be resolved to acquire the IPDL actor.
+ mProcess = new RDDProcessHost(this);
+ if (!mProcess->Launch(extraArgs)) {
+ mNumProcessAttempts++;
+ DestroyProcess();
+ return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
+ __func__);
+ }
+
+ mLaunchRDDPromise = mProcess->LaunchPromise()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [this](bool) {
+ if (!Get()) {
+ // Shutdown?
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_NOT_AVAILABLE, __func__);
+ }
+
+ mRDDChild = mProcess->GetActor();
+ mProcessToken = mProcess->GetProcessToken();
+
+ // Flush any pref updates that happened during
+ // launch and weren't included in the blobs set
+ // up in LaunchRDDProcess.
+ for (const mozilla::dom::Pref& pref : mQueuedPrefs) {
+ Unused << NS_WARN_IF(!mRDDChild->SendPreferenceUpdate(pref));
+ }
+ mQueuedPrefs.Clear();
+
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::RDDProcessStatus, "Running"_ns);
+
+ if (!CreateVideoBridge()) {
+ mNumProcessAttempts++;
+ DestroyProcess();
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_NOT_AVAILABLE, __func__);
+ }
+ return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
+ },
+ [this](nsresult aError) {
+ if (Get()) {
+ mNumProcessAttempts++;
+ DestroyProcess();
+ }
+ return GenericNonExclusivePromise::CreateAndReject(aError, __func__);
+ });
+ return mLaunchRDDPromise;
+}
+
+auto RDDProcessManager::EnsureRDDProcessAndCreateBridge(
+ base::ProcessId aOtherProcess) -> RefPtr<EnsureRDDPromise> {
+ return InvokeAsync(
+ GetMainThreadSerialEventTarget(), __func__,
+ [aOtherProcess, this]() -> RefPtr<EnsureRDDPromise> {
+ return LaunchRDDProcess()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [aOtherProcess, this]() {
+ if (!Get()) {
+ // Shutdown?
+ return EnsureRDDPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
+ __func__);
+ }
+ ipc::Endpoint<PRemoteDecoderManagerChild> endpoint;
+ if (!CreateContentBridge(aOtherProcess, &endpoint)) {
+ return EnsureRDDPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
+ __func__);
+ }
+ mNumProcessAttempts = 0;
+ return EnsureRDDPromise::CreateAndResolve(std::move(endpoint),
+ __func__);
+ },
+ [](nsresult aError) {
+ return EnsureRDDPromise::CreateAndReject(aError, __func__);
+ });
+ });
+}
+
+bool RDDProcessManager::IsRDDProcessLaunching() {
+ MOZ_ASSERT(NS_IsMainThread());
+ return !!mProcess && !mRDDChild;
+}
+
+void RDDProcessManager::OnProcessUnexpectedShutdown(RDDProcessHost* aHost) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mProcess && mProcess == aHost);
+
+ mNumUnexpectedCrashes++;
+
+ DestroyProcess();
+}
+
+void RDDProcessManager::NotifyRemoteActorDestroyed(
+ const uint64_t& aProcessToken) {
+ if (!NS_IsMainThread()) {
+ RefPtr<Runnable> task = mTaskFactory.NewRunnableMethod(
+ &RDDProcessManager::NotifyRemoteActorDestroyed, aProcessToken);
+ NS_DispatchToMainThread(task.forget());
+ return;
+ }
+
+ if (mProcessToken != aProcessToken) {
+ // This token is for an older process; we can safely ignore it.
+ return;
+ }
+
+ // One of the bridged top-level actors for the RDD process has been
+ // prematurely terminated, and we're receiving a notification. This
+ // can happen if the ActorDestroy for a bridged protocol fires
+ // before the ActorDestroy for PRDDChild.
+ OnProcessUnexpectedShutdown(mProcess);
+}
+
+void RDDProcessManager::CleanShutdown() { DestroyProcess(); }
+
+void RDDProcessManager::DestroyProcess() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!mProcess) {
+ return;
+ }
+
+ mProcess->Shutdown();
+ mProcessToken = 0;
+ mProcess = nullptr;
+ mRDDChild = nullptr;
+ mQueuedPrefs.Clear();
+
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::RDDProcessStatus, "Destroyed"_ns);
+}
+
+bool RDDProcessManager::CreateContentBridge(
+ base::ProcessId aOtherProcess,
+ ipc::Endpoint<PRemoteDecoderManagerChild>* aOutRemoteDecoderManager) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ ipc::Endpoint<PRemoteDecoderManagerParent> parentPipe;
+ ipc::Endpoint<PRemoteDecoderManagerChild> childPipe;
+
+ nsresult rv = PRemoteDecoderManager::CreateEndpoints(
+ mRDDChild->OtherPid(), aOtherProcess, &parentPipe, &childPipe);
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(sPDMLog, LogLevel::Debug,
+ ("Could not create content remote decoder: %d", int(rv)));
+ return false;
+ }
+
+ mRDDChild->SendNewContentRemoteDecoderManager(std::move(parentPipe));
+
+ *aOutRemoteDecoderManager = std::move(childPipe);
+ return true;
+}
+
+bool RDDProcessManager::CreateVideoBridge() {
+ MOZ_ASSERT(NS_IsMainThread());
+ ipc::Endpoint<PVideoBridgeParent> parentPipe;
+ ipc::Endpoint<PVideoBridgeChild> childPipe;
+
+ GPUProcessManager* gpuManager = GPUProcessManager::Get();
+ base::ProcessId gpuProcessPid = gpuManager ? gpuManager->GPUProcessPid() : -1;
+
+ // Build content device data first; this ensure that the GPU process is fully
+ // ready.
+ ContentDeviceData contentDeviceData;
+ gfxPlatform::GetPlatform()->BuildContentDeviceData(&contentDeviceData);
+
+ // The child end is the producer of video frames; the parent end is the
+ // consumer.
+ base::ProcessId childPid = RDDProcessPid();
+ base::ProcessId parentPid =
+ gpuProcessPid != -1 ? gpuProcessPid : base::GetCurrentProcId();
+
+ nsresult rv = PVideoBridge::CreateEndpoints(parentPid, childPid, &parentPipe,
+ &childPipe);
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(sPDMLog, LogLevel::Debug,
+ ("Could not create video bridge: %d", int(rv)));
+ return false;
+ }
+
+ mRDDChild->SendInitVideoBridge(std::move(childPipe),
+ mNumUnexpectedCrashes == 0, contentDeviceData);
+ if (gpuProcessPid != -1) {
+ gpuManager->InitVideoBridge(std::move(parentPipe));
+ } else {
+ VideoBridgeParent::Open(std::move(parentPipe),
+ VideoBridgeSource::RddProcess);
+ }
+
+ return true;
+}
+
+base::ProcessId RDDProcessManager::RDDProcessPid() {
+ MOZ_ASSERT(NS_IsMainThread());
+ base::ProcessId rddPid = mRDDChild ? mRDDChild->OtherPid() : -1;
+ return rddPid;
+}
+
+class RDDMemoryReporter : public MemoryReportingProcess {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RDDMemoryReporter, override)
+
+ bool IsAlive() const override { return !!GetChild(); }
+
+ bool SendRequestMemoryReport(
+ const uint32_t& aGeneration, const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<ipc::FileDescriptor>& aDMDFile) override {
+ RDDChild* child = GetChild();
+ if (!child) {
+ return false;
+ }
+
+ return child->SendRequestMemoryReport(aGeneration, aAnonymize,
+ aMinimizeMemoryUsage, aDMDFile);
+ }
+
+ int32_t Pid() const override {
+ if (RDDChild* child = GetChild()) {
+ return (int32_t)child->OtherPid();
+ }
+ return 0;
+ }
+
+ private:
+ RDDChild* GetChild() const {
+ if (RDDProcessManager* rddpm = RDDProcessManager::Get()) {
+ if (RDDChild* child = rddpm->GetRDDChild()) {
+ return child;
+ }
+ }
+ return nullptr;
+ }
+
+ protected:
+ ~RDDMemoryReporter() = default;
+};
+
+RefPtr<MemoryReportingProcess> RDDProcessManager::GetProcessMemoryReporter() {
+ if (!mProcess || !mProcess->IsConnected()) {
+ return nullptr;
+ }
+ return new RDDMemoryReporter();
+}
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RDDProcessManager.h b/dom/media/ipc/RDDProcessManager.h
new file mode 100644
index 0000000000..0204eba373
--- /dev/null
+++ b/dom/media/ipc/RDDProcessManager.h
@@ -0,0 +1,116 @@
+/* -*- 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/. */
+#ifndef _include_dom_media_ipc_RDDProcessManager_h_
+#define _include_dom_media_ipc_RDDProcessManager_h_
+#include "mozilla/MozPromise.h"
+#include "mozilla/PRemoteDecoderManagerChild.h"
+#include "mozilla/RDDProcessHost.h"
+#include "mozilla/ipc/TaskFactory.h"
+#include "nsIObserver.h"
+
+namespace mozilla {
+
+class MemoryReportingProcess;
+class RDDChild;
+
+// The RDDProcessManager is a singleton responsible for creating RDD-bound
+// objects that may live in another process. Currently, it provides access
+// to the RDD process via ContentParent.
+class RDDProcessManager final : public RDDProcessHost::Listener {
+ friend class RDDChild;
+
+ public:
+ static void Initialize();
+ static void Shutdown();
+ static RDDProcessManager* Get();
+
+ ~RDDProcessManager();
+
+ using EnsureRDDPromise =
+ MozPromise<ipc::Endpoint<PRemoteDecoderManagerChild>, nsresult, true>;
+ // Launch a new RDD process asynchronously
+ RefPtr<GenericNonExclusivePromise> LaunchRDDProcess();
+ // If not using a RDD process, launch a new RDD process asynchronously and
+ // create a RemoteDecoderManager bridge
+ RefPtr<EnsureRDDPromise> EnsureRDDProcessAndCreateBridge(
+ base::ProcessId aOtherProcess);
+
+ void OnProcessUnexpectedShutdown(RDDProcessHost* aHost) override;
+
+ // Notify the RDDProcessManager that a top-level PRDD protocol has been
+ // terminated. This may be called from any thread.
+ void NotifyRemoteActorDestroyed(const uint64_t& aProcessToken);
+
+ // Returns -1 if there is no RDD process, or the platform pid for it.
+ base::ProcessId RDDProcessPid();
+
+ // If a RDD process is present, create a MemoryReportingProcess object.
+ // Otherwise, return null.
+ RefPtr<MemoryReportingProcess> GetProcessMemoryReporter();
+
+ // Returns access to the PRDD protocol if a RDD process is present.
+ RDDChild* GetRDDChild() { return mRDDChild; }
+
+ // Returns whether or not a RDD process was ever launched.
+ bool AttemptedRDDProcess() const { return mNumProcessAttempts > 0; }
+
+ // Returns the RDD Process
+ RDDProcessHost* Process() { return mProcess; }
+
+ private:
+ bool IsRDDProcessLaunching();
+ bool CreateVideoBridge();
+
+ // Called from our xpcom-shutdown observer.
+ void OnXPCOMShutdown();
+ void OnPreferenceChange(const char16_t* aData);
+
+ RDDProcessManager();
+
+ // Shutdown the RDD process.
+ void CleanShutdown();
+ void DestroyProcess();
+
+ DISALLOW_COPY_AND_ASSIGN(RDDProcessManager);
+
+ class Observer final : public nsIObserver {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ explicit Observer(RDDProcessManager* aManager);
+
+ protected:
+ ~Observer() = default;
+
+ RDDProcessManager* mManager;
+ };
+ friend class Observer;
+
+ bool CreateContentBridge(
+ base::ProcessId aOtherProcess,
+ ipc::Endpoint<PRemoteDecoderManagerChild>* aOutRemoteDecoderManager);
+
+ const RefPtr<Observer> mObserver;
+ ipc::TaskFactory<RDDProcessManager> mTaskFactory;
+ uint32_t mNumProcessAttempts = 0;
+ uint32_t mNumUnexpectedCrashes = 0;
+
+ // Fields that are associated with the current RDD process.
+ RDDProcessHost* mProcess = nullptr;
+ uint64_t mProcessToken = 0;
+ RDDChild* mRDDChild = nullptr;
+ // Collects any pref changes that occur during process launch (after
+ // the initial map is passed in command-line arguments) to be sent
+ // when the process can receive IPC messages.
+ nsTArray<dom::Pref> mQueuedPrefs;
+ // Promise will be resolved when the RDD process has been fully started and
+ // VideoBridge configured. Only accessed on the main thread.
+ RefPtr<GenericNonExclusivePromise> mLaunchRDDPromise;
+};
+
+} // namespace mozilla
+
+#endif // _include_dom_media_ipc_RDDProcessManager_h_
diff --git a/dom/media/ipc/RemoteAudioDecoder.cpp b/dom/media/ipc/RemoteAudioDecoder.cpp
new file mode 100644
index 0000000000..48250dc0c4
--- /dev/null
+++ b/dom/media/ipc/RemoteAudioDecoder.cpp
@@ -0,0 +1,117 @@
+/* -*- 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 "RemoteAudioDecoder.h"
+
+#include "MediaDataDecoderProxy.h"
+#include "PDMFactory.h"
+#include "RemoteDecoderManagerChild.h"
+#include "RemoteDecoderManagerParent.h"
+#include "mozilla/PodOperations.h"
+
+namespace mozilla {
+
+RemoteAudioDecoderChild::RemoteAudioDecoderChild()
+ : RemoteDecoderChild(RemoteDecodeIn::RddProcess) {}
+
+MediaResult RemoteAudioDecoderChild::ProcessOutput(
+ DecodedOutputIPDL&& aDecodedData) {
+ AssertOnManagerThread();
+
+ MOZ_ASSERT(aDecodedData.type() == DecodedOutputIPDL::TArrayOfRemoteAudioData);
+ RefPtr<ArrayOfRemoteAudioData> arrayData =
+ aDecodedData.get_ArrayOfRemoteAudioData();
+
+ for (size_t i = 0; i < arrayData->Count(); i++) {
+ RefPtr<AudioData> data = arrayData->ElementAt(i);
+ if (!data) {
+ // OOM
+ return MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__);
+ }
+ mDecodedData.AppendElement(data);
+ }
+ return NS_OK;
+}
+
+MediaResult RemoteAudioDecoderChild::InitIPDL(
+ const AudioInfo& aAudioInfo,
+ const CreateDecoderParams::OptionSet& aOptions) {
+ RefPtr<RemoteDecoderManagerChild> manager =
+ RemoteDecoderManagerChild::GetSingleton(mLocation);
+
+ // The manager isn't available because RemoteDecoderManagerChild has been
+ // initialized with null end points and we don't want to decode video on RDD
+ // process anymore. Return false here so that we can fallback to other PDMs.
+ if (!manager) {
+ return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ RESULT_DETAIL("RemoteDecoderManager is not available."));
+ }
+
+ if (!manager->CanSend()) {
+ return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ RESULT_DETAIL("RemoteDecoderManager unable to send."));
+ }
+
+ mIPDLSelfRef = this;
+ Unused << manager->SendPRemoteDecoderConstructor(this, aAudioInfo, aOptions,
+ Nothing());
+ return NS_OK;
+}
+
+RemoteAudioDecoderParent::RemoteAudioDecoderParent(
+ RemoteDecoderManagerParent* aParent, const AudioInfo& aAudioInfo,
+ const CreateDecoderParams::OptionSet& aOptions,
+ nsISerialEventTarget* aManagerThread, TaskQueue* aDecodeTaskQueue)
+ : RemoteDecoderParent(aParent, aOptions, aManagerThread, aDecodeTaskQueue),
+ mAudioInfo(aAudioInfo) {}
+
+IPCResult RemoteAudioDecoderParent::RecvConstruct(
+ ConstructResolver&& aResolver) {
+ auto params = CreateDecoderParams{mAudioInfo, mOptions,
+ CreateDecoderParams::NoWrapper(true)};
+
+ mParent->EnsurePDMFactory().CreateDecoder(params)->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [resolver = std::move(aResolver), self = RefPtr{this}](
+ PlatformDecoderModule::CreateDecoderPromise::ResolveOrRejectValue&&
+ aValue) {
+ if (aValue.IsReject()) {
+ resolver(aValue.RejectValue());
+ return;
+ }
+ MOZ_ASSERT(aValue.ResolveValue());
+ self->mDecoder =
+ new MediaDataDecoderProxy(aValue.ResolveValue().forget(),
+ do_AddRef(self->mDecodeTaskQueue.get()));
+ resolver(NS_OK);
+ });
+
+ return IPC_OK();
+}
+
+MediaResult RemoteAudioDecoderParent::ProcessDecodedData(
+ MediaDataDecoder::DecodedData&& aData, DecodedOutputIPDL& aDecodedData) {
+ MOZ_ASSERT(OnManagerThread());
+
+ // Converted array to array of RefPtr<AudioData>
+ nsTArray<RefPtr<AudioData>> data(aData.Length());
+ for (auto&& element : aData) {
+ MOZ_ASSERT(element->mType == MediaData::Type::AUDIO_DATA,
+ "Can only decode audio using RemoteAudioDecoderParent!");
+ AudioData* audio = static_cast<AudioData*>(element.get());
+ data.AppendElement(audio);
+ }
+ auto array = MakeRefPtr<ArrayOfRemoteAudioData>();
+ if (!array->Fill(std::move(data),
+ [&](size_t aSize) { return AllocateBuffer(aSize); })) {
+ return MediaResult(
+ NS_ERROR_OUT_OF_MEMORY,
+ "Failed in RemoteAudioDecoderParent::ProcessDecodedData");
+ }
+ aDecodedData = std::move(array);
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RemoteAudioDecoder.h b/dom/media/ipc/RemoteAudioDecoder.h
new file mode 100644
index 0000000000..c09834eac0
--- /dev/null
+++ b/dom/media/ipc/RemoteAudioDecoder.h
@@ -0,0 +1,51 @@
+/* -*- 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/. */
+#ifndef include_dom_media_ipc_RemoteAudioDecoderChild_h
+#define include_dom_media_ipc_RemoteAudioDecoderChild_h
+#include "RemoteDecoderChild.h"
+#include "RemoteDecoderParent.h"
+
+namespace mozilla {
+
+using mozilla::ipc::IPCResult;
+
+class RemoteAudioDecoderChild final : public RemoteDecoderChild {
+ public:
+ explicit RemoteAudioDecoderChild();
+
+ MOZ_IS_CLASS_INIT
+ MediaResult InitIPDL(const AudioInfo& aAudioInfo,
+ const CreateDecoderParams::OptionSet& aOptions);
+
+ MediaResult ProcessOutput(DecodedOutputIPDL&& aDecodedData) override;
+};
+
+class RemoteAudioDecoderParent final : public RemoteDecoderParent {
+ public:
+ RemoteAudioDecoderParent(RemoteDecoderManagerParent* aParent,
+ const AudioInfo& aAudioInfo,
+ const CreateDecoderParams::OptionSet& aOptions,
+ nsISerialEventTarget* aManagerThread,
+ TaskQueue* aDecodeTaskQueue);
+
+ protected:
+ IPCResult RecvConstruct(ConstructResolver&& aResolver) override;
+ MediaResult ProcessDecodedData(MediaDataDecoder::DecodedData&& aData,
+ DecodedOutputIPDL& aDecodedData) override;
+
+ private:
+ // Can only be accessed from the manager thread
+ // Note: we can't keep a reference to the original AudioInfo here
+ // because unlike in typical MediaDataDecoder situations, we're being
+ // passed a deserialized AudioInfo from RecvPRemoteDecoderConstructor
+ // which is destroyed when RecvPRemoteDecoderConstructor returns.
+ const AudioInfo mAudioInfo;
+ const CreateDecoderParams::OptionSet mOptions;
+};
+
+} // namespace mozilla
+
+#endif // include_dom_media_ipc_RemoteAudioDecoderChild_h
diff --git a/dom/media/ipc/RemoteDecoderChild.cpp b/dom/media/ipc/RemoteDecoderChild.cpp
new file mode 100644
index 0000000000..1cc957e982
--- /dev/null
+++ b/dom/media/ipc/RemoteDecoderChild.cpp
@@ -0,0 +1,275 @@
+/* -*- 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 "RemoteDecoderChild.h"
+
+#include "RemoteDecoderManagerChild.h"
+
+namespace mozilla {
+
+RemoteDecoderChild::RemoteDecoderChild(RemoteDecodeIn aLocation)
+ : ShmemRecycleAllocator(this),
+ mLocation(aLocation),
+ mThread(GetCurrentSerialEventTarget()) {
+ MOZ_DIAGNOSTIC_ASSERT(
+ RemoteDecoderManagerChild::GetManagerThread() &&
+ RemoteDecoderManagerChild::GetManagerThread()->IsOnCurrentThread(),
+ "Must be created on the manager thread");
+}
+
+RemoteDecoderChild::~RemoteDecoderChild() = default;
+
+void RemoteDecoderChild::HandleRejectionError(
+ const ipc::ResponseRejectReason& aReason,
+ std::function<void(const MediaResult&)>&& aCallback) {
+ // If the channel goes down and CanSend() returns false, the IPDL promise will
+ // be rejected with SendError rather than ActorDestroyed. Both means the same
+ // thing and we can consider that the parent has crashed. The child can no
+ // longer be used.
+
+ // The GPU/RDD process crashed.
+ if (mLocation == RemoteDecodeIn::GpuProcess) {
+ // The GPU process will get automatically restarted by the parent process.
+ // Once it has been restarted the ContentChild will receive the message and
+ // will call GetManager()->InitForGPUProcess.
+ // We defer reporting an error until we've recreated the RemoteDecoder
+ // manager so that it'll be safe for MediaFormatReader to recreate decoders
+ RefPtr<RemoteDecoderChild> self = this;
+ GetManager()->RunWhenGPUProcessRecreated(NS_NewRunnableFunction(
+ "RemoteDecoderChild::HandleRejectionError",
+ [self, callback = std::move(aCallback)]() {
+ MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER, __func__);
+ callback(error);
+ }));
+ return;
+ }
+ // The RDD process is restarted on demand and asynchronously, we can
+ // immediately inform the caller that a new decoder is needed. The RDD will
+ // then be restarted during the new decoder creation.
+ aCallback(MediaResult(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER, __func__));
+}
+
+// ActorDestroy is called if the channel goes down while waiting for a response.
+void RemoteDecoderChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mDecodedData.Clear();
+ CleanupShmemRecycleAllocator();
+ RecordShutdownTelemetry(aWhy == AbnormalShutdown);
+}
+
+void RemoteDecoderChild::DestroyIPDL() {
+ AssertOnManagerThread();
+ MOZ_DIAGNOSTIC_ASSERT(mInitPromise.IsEmpty() && mDecodePromise.IsEmpty() &&
+ mDrainPromise.IsEmpty() &&
+ mFlushPromise.IsEmpty() &&
+ mShutdownPromise.IsEmpty(),
+ "All promises should have been rejected");
+ if (CanSend()) {
+ PRemoteDecoderChild::Send__delete__(this);
+ }
+}
+
+void RemoteDecoderChild::IPDLActorDestroyed() { mIPDLSelfRef = nullptr; }
+
+// MediaDataDecoder methods
+
+RefPtr<MediaDataDecoder::InitPromise> RemoteDecoderChild::Init() {
+ AssertOnManagerThread();
+
+ RefPtr<RemoteDecoderChild> self = this;
+ SendInit()
+ ->Then(
+ mThread, __func__,
+ [self, this](InitResultIPDL&& aResponse) {
+ mInitPromiseRequest.Complete();
+ if (aResponse.type() == InitResultIPDL::TMediaResult) {
+ mInitPromise.Reject(aResponse.get_MediaResult(), __func__);
+ return;
+ }
+ const auto& initResponse = aResponse.get_InitCompletionIPDL();
+ mDescription =
+ initResponse.decoderDescription() +
+ (GetManager()->Location() == RemoteDecodeIn::RddProcess
+ ? " (RDD remote)"_ns
+ : " (GPU remote)"_ns);
+ mIsHardwareAccelerated = initResponse.hardware();
+ mHardwareAcceleratedReason = initResponse.hardwareReason();
+ mConversion = initResponse.conversion();
+ // Either the promise has not yet been resolved or the handler has
+ // been disconnected and we can't get here.
+ mInitPromise.Resolve(initResponse.type(), __func__);
+ },
+ [self](const mozilla::ipc::ResponseRejectReason& aReason) {
+ self->mInitPromiseRequest.Complete();
+ self->HandleRejectionError(
+ aReason, [self](const MediaResult& aError) {
+ self->mInitPromise.RejectIfExists(aError, __func__);
+ });
+ })
+ ->Track(mInitPromiseRequest);
+
+ return mInitPromise.Ensure(__func__);
+}
+
+RefPtr<MediaDataDecoder::DecodePromise> RemoteDecoderChild::Decode(
+ const nsTArray<RefPtr<MediaRawData>>& aSamples) {
+ AssertOnManagerThread();
+
+ auto samples = MakeRefPtr<ArrayOfRemoteMediaRawData>();
+ if (!samples->Fill(aSamples,
+ [&](size_t aSize) { return AllocateBuffer(aSize); })) {
+ return MediaDataDecoder::DecodePromise::CreateAndReject(
+ NS_ERROR_OUT_OF_MEMORY, __func__);
+ }
+ SendDecode(samples)->Then(
+ mThread, __func__,
+ [self = RefPtr{this}, this](
+ PRemoteDecoderChild::DecodePromise::ResolveOrRejectValue&& aValue) {
+ // We no longer need the samples as the data has been
+ // processed by the parent.
+ // If the parent died, the error being fatal will cause the
+ // decoder to be torn down and all shmem in the pool will be
+ // deallocated.
+ ReleaseAllBuffers();
+
+ if (aValue.IsReject()) {
+ HandleRejectionError(
+ aValue.RejectValue(), [self](const MediaResult& aError) {
+ self->mDecodePromise.RejectIfExists(aError, __func__);
+ });
+ return;
+ }
+ MOZ_DIAGNOSTIC_ASSERT(CanSend(),
+ "The parent unexpectedly died, promise should "
+ "have been rejected first");
+ if (mDecodePromise.IsEmpty()) {
+ // We got flushed.
+ return;
+ }
+ auto response = std::move(aValue.ResolveValue());
+ if (response.type() == DecodeResultIPDL::TMediaResult &&
+ NS_FAILED(response.get_MediaResult())) {
+ mDecodePromise.Reject(response.get_MediaResult(), __func__);
+ return;
+ }
+ if (response.type() == DecodeResultIPDL::TDecodedOutputIPDL) {
+ ProcessOutput(std::move(response.get_DecodedOutputIPDL()));
+ }
+ mDecodePromise.Resolve(std::move(mDecodedData), __func__);
+ mDecodedData = MediaDataDecoder::DecodedData();
+ });
+
+ return mDecodePromise.Ensure(__func__);
+}
+
+RefPtr<MediaDataDecoder::FlushPromise> RemoteDecoderChild::Flush() {
+ AssertOnManagerThread();
+ mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+ mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+
+ RefPtr<RemoteDecoderChild> self = this;
+ SendFlush()->Then(
+ mThread, __func__,
+ [self](const MediaResult& aResult) {
+ if (NS_SUCCEEDED(aResult)) {
+ self->mFlushPromise.ResolveIfExists(true, __func__);
+ } else {
+ self->mFlushPromise.RejectIfExists(aResult, __func__);
+ }
+ },
+ [self](const mozilla::ipc::ResponseRejectReason& aReason) {
+ self->HandleRejectionError(aReason, [self](const MediaResult& aError) {
+ self->mFlushPromise.RejectIfExists(aError, __func__);
+ });
+ });
+ return mFlushPromise.Ensure(__func__);
+}
+
+RefPtr<MediaDataDecoder::DecodePromise> RemoteDecoderChild::Drain() {
+ AssertOnManagerThread();
+
+ RefPtr<RemoteDecoderChild> self = this;
+ SendDrain()->Then(
+ mThread, __func__,
+ [self, this](DecodeResultIPDL&& aResponse) {
+ if (mDrainPromise.IsEmpty()) {
+ // We got flushed.
+ return;
+ }
+ if (aResponse.type() == DecodeResultIPDL::TMediaResult &&
+ NS_FAILED(aResponse.get_MediaResult())) {
+ mDrainPromise.Reject(aResponse.get_MediaResult(), __func__);
+ return;
+ }
+ MOZ_DIAGNOSTIC_ASSERT(CanSend(),
+ "The parent unexpectedly died, promise should "
+ "have been rejected first");
+ if (aResponse.type() == DecodeResultIPDL::TDecodedOutputIPDL) {
+ ProcessOutput(std::move(aResponse.get_DecodedOutputIPDL()));
+ }
+ mDrainPromise.Resolve(std::move(mDecodedData), __func__);
+ mDecodedData = MediaDataDecoder::DecodedData();
+ },
+ [self](const mozilla::ipc::ResponseRejectReason& aReason) {
+ self->HandleRejectionError(aReason, [self](const MediaResult& aError) {
+ self->mDrainPromise.RejectIfExists(aError, __func__);
+ });
+ });
+ return mDrainPromise.Ensure(__func__);
+}
+
+RefPtr<mozilla::ShutdownPromise> RemoteDecoderChild::Shutdown() {
+ AssertOnManagerThread();
+ // Shutdown() can be called while an InitPromise is pending.
+ mInitPromiseRequest.DisconnectIfExists();
+ mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+ mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+ mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+ mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+
+ RefPtr<RemoteDecoderChild> self = this;
+ SendShutdown()->Then(
+ mThread, __func__,
+ [self](
+ PRemoteDecoderChild::ShutdownPromise::ResolveOrRejectValue&& aValue) {
+ self->mShutdownPromise.Resolve(aValue.IsResolve(), __func__);
+ });
+ return mShutdownPromise.Ensure(__func__);
+}
+
+bool RemoteDecoderChild::IsHardwareAccelerated(
+ nsACString& aFailureReason) const {
+ AssertOnManagerThread();
+ aFailureReason = mHardwareAcceleratedReason;
+ return mIsHardwareAccelerated;
+}
+
+nsCString RemoteDecoderChild::GetDescriptionName() const {
+ AssertOnManagerThread();
+ return mDescription;
+}
+
+void RemoteDecoderChild::SetSeekThreshold(const media::TimeUnit& aTime) {
+ AssertOnManagerThread();
+ Unused << SendSetSeekThreshold(aTime);
+}
+
+MediaDataDecoder::ConversionRequired RemoteDecoderChild::NeedsConversion()
+ const {
+ AssertOnManagerThread();
+ return mConversion;
+}
+
+void RemoteDecoderChild::AssertOnManagerThread() const {
+ MOZ_ASSERT(mThread->IsOnCurrentThread());
+}
+
+RemoteDecoderManagerChild* RemoteDecoderChild::GetManager() {
+ if (!CanSend()) {
+ return nullptr;
+ }
+ return static_cast<RemoteDecoderManagerChild*>(Manager());
+}
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RemoteDecoderChild.h b/dom/media/ipc/RemoteDecoderChild.h
new file mode 100644
index 0000000000..7d9602497a
--- /dev/null
+++ b/dom/media/ipc/RemoteDecoderChild.h
@@ -0,0 +1,84 @@
+/* -*- 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/. */
+#ifndef include_dom_media_ipc_RemoteDecoderChild_h
+#define include_dom_media_ipc_RemoteDecoderChild_h
+
+#include <functional>
+
+#include "mozilla/PRemoteDecoderChild.h"
+#include "mozilla/ShmemRecycleAllocator.h"
+
+namespace mozilla {
+
+class RemoteDecoderManagerChild;
+using mozilla::MediaDataDecoder;
+using mozilla::ipc::IPCResult;
+
+class RemoteDecoderChild : public ShmemRecycleAllocator<RemoteDecoderChild>,
+ public PRemoteDecoderChild {
+ friend class PRemoteDecoderChild;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteDecoderChild);
+
+ explicit RemoteDecoderChild(RemoteDecodeIn aLocation);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // This interface closely mirrors the MediaDataDecoder plus a bit
+ // (DestroyIPDL) to allow proxying to a remote decoder in RemoteDecoderModule.
+ RefPtr<MediaDataDecoder::InitPromise> Init();
+ RefPtr<MediaDataDecoder::DecodePromise> Decode(
+ const nsTArray<RefPtr<MediaRawData>>& aSamples);
+ RefPtr<MediaDataDecoder::DecodePromise> Drain();
+ RefPtr<MediaDataDecoder::FlushPromise> Flush();
+ RefPtr<mozilla::ShutdownPromise> Shutdown();
+ bool IsHardwareAccelerated(nsACString& aFailureReason) const;
+ nsCString GetDescriptionName() const;
+ void SetSeekThreshold(const media::TimeUnit& aTime);
+ MediaDataDecoder::ConversionRequired NeedsConversion() const;
+ void DestroyIPDL();
+
+ // Called from IPDL when our actor has been destroyed
+ void IPDLActorDestroyed();
+
+ RemoteDecoderManagerChild* GetManager();
+
+ protected:
+ virtual ~RemoteDecoderChild();
+ void AssertOnManagerThread() const;
+
+ virtual MediaResult ProcessOutput(DecodedOutputIPDL&& aDecodedData) = 0;
+ virtual void RecordShutdownTelemetry(bool aForAbnormalShutdown) {}
+
+ RefPtr<RemoteDecoderChild> mIPDLSelfRef;
+ MediaDataDecoder::DecodedData mDecodedData;
+ const RemoteDecodeIn mLocation;
+
+ private:
+ const nsCOMPtr<nsISerialEventTarget> mThread;
+
+ MozPromiseHolder<MediaDataDecoder::InitPromise> mInitPromise;
+ MozPromiseRequestHolder<PRemoteDecoderChild::InitPromise> mInitPromiseRequest;
+ MozPromiseHolder<MediaDataDecoder::DecodePromise> mDecodePromise;
+ MozPromiseHolder<MediaDataDecoder::DecodePromise> mDrainPromise;
+ MozPromiseHolder<MediaDataDecoder::FlushPromise> mFlushPromise;
+ MozPromiseHolder<mozilla::ShutdownPromise> mShutdownPromise;
+
+ void HandleRejectionError(
+ const ipc::ResponseRejectReason& aReason,
+ std::function<void(const MediaResult&)>&& aCallback);
+
+ nsCString mHardwareAcceleratedReason;
+ nsCString mDescription;
+ bool mIsHardwareAccelerated = false;
+ MediaDataDecoder::ConversionRequired mConversion =
+ MediaDataDecoder::ConversionRequired::kNeedNone;
+};
+
+} // namespace mozilla
+
+#endif // include_dom_media_ipc_RemoteDecoderChild_h
diff --git a/dom/media/ipc/RemoteDecoderManagerChild.cpp b/dom/media/ipc/RemoteDecoderManagerChild.cpp
new file mode 100644
index 0000000000..50e80e7a33
--- /dev/null
+++ b/dom/media/ipc/RemoteDecoderManagerChild.cpp
@@ -0,0 +1,619 @@
+/* -*- 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/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 "nsContentUtils.h"
+#include "nsIObserver.h"
+
+namespace mozilla {
+
+using namespace layers;
+using namespace gfx;
+
+// Used so that we only ever attempt to check if the RDD process should be
+// launched serially. Protects sLaunchPromise
+StaticMutex sLaunchMutex;
+static StaticRefPtr<GenericNonExclusivePromise> sLaunchRDDPromise;
+
+// 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<StaticRefPtr<nsIThread>>
+ sRemoteDecoderManagerChildThread("sRemoteDecoderManagerChildThread");
+
+// Only accessed from sRemoteDecoderManagerChildThread
+static StaticRefPtr<RemoteDecoderManagerChild>
+ sRemoteDecoderManagerChildForRDDProcess;
+
+static StaticRefPtr<RemoteDecoderManagerChild>
+ sRemoteDecoderManagerChildForGPUProcess;
+static UniquePtr<nsTArray<RefPtr<Runnable>>> sRecreateTasks;
+
+static StaticDataMutex<Maybe<PDMFactory::MediaCodecsSupported>> sGPUSupported(
+ "RDMC::sGPUSupported");
+static StaticDataMutex<Maybe<PDMFactory::MediaCodecsSupported>> sRDDSupported(
+ "RDMC::sRDDSupported");
+
+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<ShutdownObserver> sObserver;
+
+/* static */
+void RemoteDecoderManagerChild::Init() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ auto remoteDecoderManagerThread = sRemoteDecoderManagerChildThread.Lock();
+ if (!*remoteDecoderManagerThread) {
+ // 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<nsIThread> childThread;
+ nsresult rv = NS_NewNamedThread(
+ "RemVidChild", getter_AddRefs(childThread),
+ NS_NewRunnableFunction(
+ "RemoteDecoderManagerChild::InitPBackground", []() {
+ ipc::PBackgroundChild* bgActor =
+ ipc::BackgroundChild::GetOrCreateForCurrentThread();
+ NS_ASSERTION(bgActor, "Failed to start Background channel");
+ Unused << bgActor;
+ }));
+
+ NS_ENSURE_SUCCESS_VOID(rv);
+ *remoteDecoderManagerThread = childThread;
+ sRecreateTasks = MakeUnique<nsTArray<RefPtr<Runnable>>>();
+ sObserver = new ShutdownObserver();
+ nsContentUtils::RegisterShutdownObserver(sObserver);
+ }
+}
+
+/* static */
+void RemoteDecoderManagerChild::InitForGPUProcess(
+ Endpoint<PRemoteDecoderManagerChild>&& aVideoManager) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ Init();
+
+ auto remoteDecoderManagerThread = sRemoteDecoderManagerChildThread.Lock();
+ MOZ_ALWAYS_SUCCEEDS((*remoteDecoderManagerThread)
+ ->Dispatch(NewRunnableFunction(
+ "InitForContentRunnable", &OpenForGPUProcess,
+ std::move(aVideoManager))));
+}
+
+/* static */
+void RemoteDecoderManagerChild::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (sObserver) {
+ nsContentUtils::UnregisterShutdownObserver(sObserver);
+ sObserver = nullptr;
+ }
+
+ nsCOMPtr<nsIThread> childThread;
+ {
+ auto remoteDecoderManagerThread = sRemoteDecoderManagerChildThread.Lock();
+ childThread = remoteDecoderManagerThread->forget();
+ }
+ if (childThread) {
+ MOZ_ALWAYS_SUCCEEDS(childThread->Dispatch(NS_NewRunnableFunction(
+ "dom::RemoteDecoderManagerChild::Shutdown", []() {
+ if (sRemoteDecoderManagerChildForRDDProcess &&
+ sRemoteDecoderManagerChildForRDDProcess->CanSend()) {
+ sRemoteDecoderManagerChildForRDDProcess->Close();
+ }
+ sRemoteDecoderManagerChildForRDDProcess = nullptr;
+ if (sRemoteDecoderManagerChildForGPUProcess &&
+ sRemoteDecoderManagerChildForGPUProcess->CanSend()) {
+ sRemoteDecoderManagerChildForGPUProcess->Close();
+ }
+ sRemoteDecoderManagerChildForGPUProcess = nullptr;
+ ipc::BackgroundChild::CloseForCurrentThread();
+ })));
+ childThread->Shutdown();
+ sRecreateTasks = nullptr;
+ }
+}
+
+void RemoteDecoderManagerChild::RunWhenGPUProcessRecreated(
+ already_AddRefed<Runnable> aTask) {
+ nsCOMPtr<nsISerialEventTarget> 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<Runnable> task = aTask;
+ task->Run();
+ } else {
+ sRecreateTasks->AppendElement(aTask);
+ }
+}
+
+/* static */
+RemoteDecoderManagerChild* RemoteDecoderManagerChild::GetSingleton(
+ RemoteDecodeIn aLocation) {
+ nsCOMPtr<nsISerialEventTarget> managerThread = GetManagerThread();
+ if (!managerThread) {
+ // We've been shutdown, bail.
+ return nullptr;
+ }
+ MOZ_ASSERT(managerThread->IsOnCurrentThread());
+ switch (aLocation) {
+ case RemoteDecodeIn::GpuProcess:
+ return sRemoteDecoderManagerChildForGPUProcess;
+ case RemoteDecodeIn::RddProcess:
+ return sRemoteDecoderManagerChildForRDDProcess;
+ default:
+ MOZ_CRASH("Unexpected RemoteDecode variant");
+ }
+}
+
+/* static */
+nsISerialEventTarget* RemoteDecoderManagerChild::GetManagerThread() {
+ auto remoteDecoderManagerThread = sRemoteDecoderManagerChildThread.Lock();
+ return *remoteDecoderManagerThread;
+}
+
+/* static */
+bool RemoteDecoderManagerChild::Supports(
+ RemoteDecodeIn aLocation, const SupportDecoderParams& aParams,
+ DecoderDoctorDiagnostics* aDiagnostics) {
+ Maybe<PDMFactory::MediaCodecsSupported> supported;
+ switch (aLocation) {
+ case RemoteDecodeIn::RddProcess: {
+ auto supportedRDD = sRDDSupported.Lock();
+ supported = *supportedRDD;
+ break;
+ }
+ case RemoteDecodeIn::GpuProcess: {
+ auto supportedGPU = sGPUSupported.Lock();
+ supported = *supportedGPU;
+ break;
+ }
+ default:
+ return false;
+ }
+ if (!supported) {
+ // We haven't received the correct information yet from either the GPU or
+ // the RDD process; assume it is supported to prevent false negative.
+ if (aLocation == RemoteDecodeIn::RddProcess) {
+ // Ensure the RDD process got started.
+ // TODO: This can be removed once bug 1684991 is fixed.
+ LaunchRDDProcessIfNeeded();
+ }
+ return true;
+ }
+
+ // 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);
+}
+
+/* static */
+RefPtr<PlatformDecoderModule::CreateDecoderPromise>
+RemoteDecoderManagerChild::CreateAudioDecoder(
+ const CreateDecoderParams& aParams) {
+ nsCOMPtr<nsISerialEventTarget> managerThread = GetManagerThread();
+ if (!managerThread) {
+ // We got shutdown.
+ return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
+ NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+ }
+ return LaunchRDDProcessIfNeeded()->Then(
+ managerThread, __func__,
+ [params = CreateDecoderParamsForAsync(aParams)](bool) {
+ auto child = MakeRefPtr<RemoteAudioDecoderChild>();
+ MediaResult result =
+ child->InitIPDL(params.AudioConfig(), params.mOptions);
+ if (NS_FAILED(result)) {
+ return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
+ result, __func__);
+ }
+ return Construct(std::move(child));
+ },
+ [](nsresult aResult) {
+ return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
+ MediaResult(aResult, "Couldn't start RDD process"), __func__);
+ });
+}
+
+/* static */
+RefPtr<PlatformDecoderModule::CreateDecoderPromise>
+RemoteDecoderManagerChild::CreateVideoDecoder(
+ const CreateDecoderParams& aParams, RemoteDecodeIn aLocation) {
+ nsCOMPtr<nsISerialEventTarget> 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__);
+ }
+
+ MOZ_ASSERT(aLocation != RemoteDecodeIn::Unspecified);
+
+ RefPtr<GenericNonExclusivePromise> p =
+ aLocation == RemoteDecodeIn::GpuProcess
+ ? GenericNonExclusivePromise::CreateAndResolve(true, __func__)
+ : LaunchRDDProcessIfNeeded();
+
+ return p->Then(
+ managerThread, __func__,
+ [aLocation, params = CreateDecoderParamsForAsync(aParams)](bool) {
+ auto child = MakeRefPtr<RemoteVideoDecoderChild>(aLocation);
+ MediaResult result = child->InitIPDL(
+ params.VideoConfig(), params.mRate.mValue, params.mOptions,
+ params.mKnowsCompositor
+ ? Some(params.mKnowsCompositor->GetTextureFactoryIdentifier())
+ : Nothing());
+ if (NS_FAILED(result)) {
+ return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
+ result, __func__);
+ }
+ return Construct(std::move(child));
+ },
+ [](nsresult aResult) {
+ return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
+ MediaResult(aResult, "Couldn't start RDD process"), __func__);
+ });
+}
+
+/* static */
+RefPtr<PlatformDecoderModule::CreateDecoderPromise>
+RemoteDecoderManagerChild::Construct(RefPtr<RemoteDecoderChild>&& aChild) {
+ nsCOMPtr<nsISerialEventTarget> managerThread = GetManagerThread();
+ if (!managerThread) {
+ // We got shutdown.
+ return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
+ NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+ }
+ MOZ_ASSERT(managerThread->IsOnCurrentThread());
+
+ RefPtr<PlatformDecoderModule::CreateDecoderPromise> 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<RemoteMediaDataDecoder>(child),
+ __func__);
+ },
+ [](const mozilla::ipc::ResponseRejectReason& aReason) {
+ // The parent has died.
+ return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
+ NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER, __func__);
+ });
+ return p;
+}
+
+/* static */
+RefPtr<GenericNonExclusivePromise>
+RemoteDecoderManagerChild::LaunchRDDProcessIfNeeded() {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess(),
+ "Only supported from a content process.");
+
+ nsCOMPtr<nsISerialEventTarget> managerThread = GetManagerThread();
+ if (!managerThread) {
+ // We got shutdown.
+ return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
+ __func__);
+ }
+
+ StaticMutexAutoLock lock(sLaunchMutex);
+
+ if (sLaunchRDDPromise) {
+ return sLaunchRDDPromise;
+ }
+
+ // 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<GenericNonExclusivePromise> p = InvokeAsync(
+ managerThread, __func__, []() -> RefPtr<GenericNonExclusivePromise> {
+ auto* rps = GetSingleton(RemoteDecodeIn::RddProcess);
+ if (rps && rps->CanSend()) {
+ return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
+ }
+ nsCOMPtr<nsISerialEventTarget> 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<nsISerialEventTarget> managerThread = GetManagerThread();
+ if (!managerThread || aResult.IsReject()) {
+ // The parent process died or we got shutdown
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_FAILURE, __func__);
+ }
+ nsresult rv = Get<0>(aResult.ResolveValue());
+ if (NS_FAILED(rv)) {
+ return GenericNonExclusivePromise::CreateAndReject(rv,
+ __func__);
+ }
+ OpenForRDDProcess(Get<1>(std::move(aResult.ResolveValue())));
+ return GenericNonExclusivePromise::CreateAndResolve(true,
+ __func__);
+ });
+ });
+
+ p = p->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [](const GenericNonExclusivePromise::ResolveOrRejectValue& aResult) {
+ StaticMutexAutoLock lock(sLaunchMutex);
+ sLaunchRDDPromise = nullptr;
+ return GenericNonExclusivePromise::CreateAndResolveOrReject(aResult,
+ __func__);
+ });
+ sLaunchRDDPromise = p;
+ return sLaunchRDDPromise;
+}
+
+PRemoteDecoderChild* RemoteDecoderManagerChild::AllocPRemoteDecoderChild(
+ const RemoteDecoderInfoIPDL& /* not used */,
+ const CreateDecoderParams::OptionSet& aOptions,
+ const Maybe<layers::TextureFactoryIdentifier>& aIdentifier) {
+ // 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<RemoteDecoderChild*>(actor);
+ child->IPDLActorDestroyed();
+ return true;
+}
+
+RemoteDecoderManagerChild::RemoteDecoderManagerChild(RemoteDecodeIn aLocation)
+ : mLocation(aLocation) {}
+
+void RemoteDecoderManagerChild::OpenForRDDProcess(
+ Endpoint<PRemoteDecoderManagerChild>&& aEndpoint) {
+ nsCOMPtr<nsISerialEventTarget> managerThread = GetManagerThread();
+ if (!managerThread) {
+ // We've been shutdown, bail.
+ return;
+ }
+ MOZ_ASSERT(managerThread->IsOnCurrentThread());
+
+ // Only create RemoteDecoderManagerChild, bind new endpoint and init
+ // ipdl if:
+ // 1) haven't init'd sRemoteDecoderManagerChild
+ // or
+ // 2) if ActorDestroy was called meaning the other end of the ipc channel was
+ // torn down
+ if (sRemoteDecoderManagerChildForRDDProcess &&
+ sRemoteDecoderManagerChildForRDDProcess->CanSend()) {
+ return;
+ }
+ sRemoteDecoderManagerChildForRDDProcess = nullptr;
+ if (aEndpoint.IsValid()) {
+ RefPtr<RemoteDecoderManagerChild> manager =
+ new RemoteDecoderManagerChild(RemoteDecodeIn::RddProcess);
+ if (aEndpoint.Bind(manager)) {
+ sRemoteDecoderManagerChildForRDDProcess = manager;
+ manager->InitIPDL();
+ }
+ }
+}
+
+void RemoteDecoderManagerChild::OpenForGPUProcess(
+ Endpoint<PRemoteDecoderManagerChild>&& aEndpoint) {
+ nsCOMPtr<nsISerialEventTarget> managerThread = GetManagerThread();
+ if (!managerThread) {
+ // We've been shutdown, bail.
+ return;
+ }
+ MOZ_ASSERT(managerThread->IsOnCurrentThread());
+ // 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.
+ sRemoteDecoderManagerChildForGPUProcess = nullptr;
+ if (aEndpoint.IsValid()) {
+ RefPtr<RemoteDecoderManagerChild> manager =
+ new RemoteDecoderManagerChild(RemoteDecodeIn::GpuProcess);
+ if (aEndpoint.Bind(manager)) {
+ sRemoteDecoderManagerChildForGPUProcess = manager;
+ manager->InitIPDL();
+ }
+ }
+ for (Runnable* task : *sRecreateTasks) {
+ task->Run();
+ }
+ sRecreateTasks->Clear();
+}
+
+void RemoteDecoderManagerChild::InitIPDL() { mIPDLSelfRef = this; }
+
+void RemoteDecoderManagerChild::ActorDealloc() { mIPDLSelfRef = nullptr; }
+
+VideoBridgeSource RemoteDecoderManagerChild::GetSource() const {
+ switch (mLocation) {
+ case RemoteDecodeIn::RddProcess:
+ return VideoBridgeSource::RddProcess;
+ case RemoteDecodeIn::GpuProcess:
+ return VideoBridgeSource::GpuProcess;
+ default:
+ MOZ_CRASH("Unexpected RemoteDecode variant");
+ }
+}
+
+bool RemoteDecoderManagerChild::DeallocShmem(mozilla::ipc::Shmem& aShmem) {
+ nsCOMPtr<nsISerialEventTarget> 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<RemoteDecoderManagerChild> mAllocator;
+ SurfaceDescriptor mSD;
+};
+
+void DeleteSurfaceDescriptorUserData(void* aClosure) {
+ SurfaceDescriptorUserData* sd =
+ reinterpret_cast<SurfaceDescriptorUserData*>(aClosure);
+ delete sd;
+}
+
+already_AddRefed<SourceSurface> RemoteDecoderManagerChild::Readback(
+ const SurfaceDescriptorGPUVideo& aSD) {
+ // We can't use NS_DISPATCH_SYNC here since that can spin the event
+ // loop while it waits. This function can be called from JS and we
+ // don't want that to happen.
+ nsCOMPtr<nsISerialEventTarget> managerThread = GetManagerThread();
+ if (!managerThread) {
+ return nullptr;
+ }
+
+ SurfaceDescriptor sd;
+ RefPtr<Runnable> task =
+ NS_NewRunnableFunction("RemoteDecoderManagerChild::Readback", [&]() {
+ if (CanSend()) {
+ SendReadback(aSD, &sd);
+ }
+ });
+ SyncRunnable::DispatchToThread(managerThread, task);
+
+ if (!IsSurfaceDescriptorValid(sd)) {
+ return nullptr;
+ }
+
+ RefPtr<DataSourceSurface> 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<nsISerialEventTarget> 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) const {
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
+}
+
+void RemoteDecoderManagerChild::SetSupported(
+ RemoteDecodeIn aLocation,
+ const PDMFactory::MediaCodecsSupported& aSupported) {
+ switch (aLocation) {
+ case RemoteDecodeIn::GpuProcess: {
+ auto supported = sGPUSupported.Lock();
+ *supported = Some(aSupported);
+ break;
+ }
+ case RemoteDecodeIn::RddProcess: {
+ auto supported = sRDDSupported.Lock();
+ *supported = Some(aSupported);
+ break;
+ }
+ default:
+ MOZ_CRASH("Not to be used for any other process");
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RemoteDecoderManagerChild.h b/dom/media/ipc/RemoteDecoderManagerChild.h
new file mode 100644
index 0000000000..ea65ebc86f
--- /dev/null
+++ b/dom/media/ipc/RemoteDecoderManagerChild.h
@@ -0,0 +1,132 @@
+/* -*- 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/. */
+#ifndef include_dom_media_ipc_RemoteDecoderManagerChild_h
+#define include_dom_media_ipc_RemoteDecoderManagerChild_h
+
+#include "GPUVideoImage.h"
+#include "ipc/EnumSerializer.h"
+#include "mozilla/EnumTypeTraits.h"
+#include "mozilla/PRemoteDecoderManagerChild.h"
+#include "mozilla/layers/VideoBridgeUtils.h"
+
+namespace mozilla {
+
+class RemoteDecoderChild;
+
+enum class RemoteDecodeIn {
+ Unspecified,
+ RddProcess,
+ GpuProcess,
+
+ SENTINEL,
+};
+
+class RemoteDecoderManagerChild final
+ : public PRemoteDecoderManagerChild,
+ public mozilla::ipc::IShmemAllocator,
+ public mozilla::layers::IGPUVideoSurfaceManager {
+ friend class PRemoteDecoderManagerChild;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteDecoderManagerChild, override)
+
+ // Can only be called from the manager thread
+ static RemoteDecoderManagerChild* GetSingleton(RemoteDecodeIn aLocation);
+
+ static void Init();
+ static void SetSupported(RemoteDecodeIn aLocation,
+ const PDMFactory::MediaCodecsSupported& aSupported);
+
+ // Can be called from any thread.
+ static bool Supports(RemoteDecodeIn aLocation,
+ const SupportDecoderParams& aParams,
+ DecoderDoctorDiagnostics* aDiagnostics);
+ static RefPtr<PlatformDecoderModule::CreateDecoderPromise> CreateAudioDecoder(
+ const CreateDecoderParams& aParams);
+ static RefPtr<PlatformDecoderModule::CreateDecoderPromise> CreateVideoDecoder(
+ const CreateDecoderParams& aParams, RemoteDecodeIn aLocation);
+
+ // Can be called from any thread.
+ static nsISerialEventTarget* GetManagerThread();
+
+ // Can be called from any thread, dispatches the request to the IPDL thread
+ // internally and will be ignored if the IPDL actor has been destroyed.
+ already_AddRefed<gfx::SourceSurface> Readback(
+ const SurfaceDescriptorGPUVideo& aSD) override;
+ void DeallocateSurfaceDescriptor(
+ const SurfaceDescriptorGPUVideo& aSD) override;
+
+ bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override {
+ return PRemoteDecoderManagerChild::AllocShmem(aSize, aShmType, aShmem);
+ }
+ bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override {
+ return PRemoteDecoderManagerChild::AllocUnsafeShmem(aSize, aShmType,
+ aShmem);
+ }
+
+ // Can be called from any thread, dispatches the request to the IPDL thread
+ // internally and will be ignored if the IPDL actor has been destroyed.
+ bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ // Main thread only
+ static void InitForGPUProcess(
+ Endpoint<PRemoteDecoderManagerChild>&& aVideoManager);
+ static void Shutdown();
+
+ // Run aTask (on the manager thread) when we next attempt to create a new
+ // manager (even if creation fails). Intended to be called from ActorDestroy
+ // when we get notified that the old manager is being destroyed. Can only be
+ // called from the manager thread.
+ void RunWhenGPUProcessRecreated(already_AddRefed<Runnable> aTask);
+
+ RemoteDecodeIn Location() const { return mLocation; }
+ layers::VideoBridgeSource GetSource() const;
+
+ protected:
+ void InitIPDL();
+
+ void ActorDealloc() override;
+
+ void HandleFatalError(const char* aMsg) const override;
+
+ PRemoteDecoderChild* AllocPRemoteDecoderChild(
+ const RemoteDecoderInfoIPDL& aRemoteDecoderInfo,
+ const CreateDecoderParams::OptionSet& aOptions,
+ const Maybe<layers::TextureFactoryIdentifier>& aIdentifier);
+ bool DeallocPRemoteDecoderChild(PRemoteDecoderChild* actor);
+
+ private:
+ explicit RemoteDecoderManagerChild(RemoteDecodeIn aLocation);
+ ~RemoteDecoderManagerChild() = default;
+ static RefPtr<PlatformDecoderModule::CreateDecoderPromise> Construct(
+ RefPtr<RemoteDecoderChild>&& aChild);
+
+ static void OpenForRDDProcess(
+ Endpoint<PRemoteDecoderManagerChild>&& aEndpoint);
+ static void OpenForGPUProcess(
+ Endpoint<PRemoteDecoderManagerChild>&& aEndpoint);
+ static RefPtr<GenericNonExclusivePromise> LaunchRDDProcessIfNeeded();
+
+ RefPtr<RemoteDecoderManagerChild> mIPDLSelfRef;
+ // The location for decoding, Rdd or Gpu process.
+ const RemoteDecodeIn mLocation;
+};
+
+} // namespace mozilla
+
+namespace IPC {
+template <>
+struct ParamTraits<mozilla::RemoteDecodeIn>
+ : public ContiguousEnumSerializer<mozilla::RemoteDecodeIn,
+ mozilla::RemoteDecodeIn::Unspecified,
+ mozilla::RemoteDecodeIn::SENTINEL> {};
+} // namespace IPC
+
+#endif // include_dom_media_ipc_RemoteDecoderManagerChild_h
diff --git a/dom/media/ipc/RemoteDecoderManagerParent.cpp b/dom/media/ipc/RemoteDecoderManagerParent.cpp
new file mode 100644
index 0000000000..8dc23bd34a
--- /dev/null
+++ b/dom/media/ipc/RemoteDecoderManagerParent.cpp
@@ -0,0 +1,295 @@
+/* -*- 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/SyncRunnable.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/VideoBridgeChild.h"
+
+namespace mozilla {
+
+#ifdef XP_WIN
+extern const nsCString GetFoundD3D11BlacklistedDLL();
+extern const nsCString GetFoundD3D9BlacklistedDLL();
+#endif // XP_WIN
+
+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 = new TaskQueue(
+ 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) {
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_RDD ||
+ XRE_GetProcessType() == GeckoProcessType_GPU);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!StartupThreads()) {
+ return false;
+ }
+
+ RefPtr<RemoteDecoderManagerParent> parent =
+ new RemoteDecoderManagerParent(sRemoteDecoderManagerParentThread);
+
+ 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) {
+ // We never want to decode in the GPU process, but output
+ // frames to the parent process.
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_RDD);
+ 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)
+ : mThread(aThread) {
+ MOZ_COUNT_CTOR(RemoteDecoderManagerParent);
+ auto& registrar = XRE_IsGPUProcess()
+ ? GPUParent::GetSingleton()->AsyncShutdownService()
+ : RDDParent::GetSingleton()->AsyncShutdownService();
+ registrar.Register(this);
+}
+
+RemoteDecoderManagerParent::~RemoteDecoderManagerParent() {
+ MOZ_COUNT_DTOR(RemoteDecoderManagerParent);
+ auto& registrar = XRE_IsGPUProcess()
+ ? GPUParent::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) {
+ RefPtr<TaskQueue> decodeTaskQueue =
+ new TaskQueue(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);
+ }
+
+ if (aRemoteDecoderInfo.type() == RemoteDecoderInfoIPDL::TAudioInfo) {
+ return new RemoteAudioDecoderParent(
+ this, aRemoteDecoderInfo.get_AudioInfo(), aOptions,
+ sRemoteDecoderManagerParentThread, decodeTaskQueue);
+ }
+
+ MOZ_CRASH("unrecognized type of RemoteDecoderInfoIPDL union");
+ return nullptr;
+}
+
+bool RemoteDecoderManagerParent::DeallocPRemoteDecoderParent(
+ PRemoteDecoderParent* actor) {
+ RemoteDecoderParent* parent = static_cast<RemoteDecoderParent*>(actor);
+ parent->Destroy();
+ 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");
+ }
+ AddRef();
+}
+
+void RemoteDecoderManagerParent::ActorDealloc() { Release(); }
+
+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();
+ }
+
+ 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, Shmem::SharedMemory::TYPE_BASIC, &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, true),
+ 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);
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RemoteDecoderManagerParent.h b/dom/media/ipc/RemoteDecoderManagerParent.h
new file mode 100644
index 0000000000..7cd2084824
--- /dev/null
+++ b/dom/media/ipc/RemoteDecoderManagerParent.h
@@ -0,0 +1,86 @@
+/* -*- 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/. */
+#ifndef include_dom_media_ipc_RemoteDecoderManagerParent_h
+#define include_dom_media_ipc_RemoteDecoderManagerParent_h
+
+#include "GPUVideoImage.h"
+#include "mozilla/PRemoteDecoderManagerParent.h"
+#include "mozilla/layers/VideoBridgeChild.h"
+
+namespace mozilla {
+
+class PDMFactory;
+
+class RemoteDecoderManagerParent final
+ : public PRemoteDecoderManagerParent,
+ public layers::IGPUVideoSurfaceManager {
+ friend class PRemoteDecoderManagerParent;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteDecoderManagerParent, override)
+
+ static bool CreateForContent(
+ Endpoint<PRemoteDecoderManagerParent>&& aEndpoint);
+
+ static bool CreateVideoBridgeToOtherProcess(
+ Endpoint<layers::PVideoBridgeChild>&& aEndpoint);
+
+ // Must be called on manager thread.
+ // Store the image so that it can be used out of process. Will be released
+ // when DeallocateSurfaceDescriptor is called.
+ void StoreImage(const SurfaceDescriptorGPUVideo& aSD, layers::Image* aImage,
+ layers::TextureClient* aTexture);
+
+ // IGPUVideoSurfaceManager methods
+ already_AddRefed<gfx::SourceSurface> Readback(
+ const SurfaceDescriptorGPUVideo& aSD) override {
+ MOZ_ASSERT_UNREACHABLE("Not usable from the parent");
+ return nullptr;
+ }
+ void DeallocateSurfaceDescriptor(
+ const SurfaceDescriptorGPUVideo& aSD) override;
+
+ static bool StartupThreads();
+ static void ShutdownThreads();
+
+ static void ShutdownVideoBridge();
+
+ bool OnManagerThread();
+
+ // Can be called from manager thread only
+ PDMFactory& EnsurePDMFactory();
+
+ protected:
+ PRemoteDecoderParent* AllocPRemoteDecoderParent(
+ const RemoteDecoderInfoIPDL& aRemoteDecoderInfo,
+ const CreateDecoderParams::OptionSet& aOptions,
+ const Maybe<layers::TextureFactoryIdentifier>& aIdentifier);
+ bool DeallocPRemoteDecoderParent(PRemoteDecoderParent* actor);
+
+ mozilla::ipc::IPCResult RecvReadback(const SurfaceDescriptorGPUVideo& aSD,
+ SurfaceDescriptor* aResult);
+ mozilla::ipc::IPCResult RecvDeallocateSurfaceDescriptorGPUVideo(
+ const SurfaceDescriptorGPUVideo& aSD);
+
+ void ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override;
+ void ActorDealloc() override;
+
+ private:
+ explicit RemoteDecoderManagerParent(nsISerialEventTarget* aThread);
+ ~RemoteDecoderManagerParent();
+
+ void Open(Endpoint<PRemoteDecoderManagerParent>&& aEndpoint);
+
+ std::map<uint64_t, RefPtr<layers::Image>> mImageMap;
+ std::map<uint64_t, RefPtr<layers::TextureClient>> mTextureMap;
+
+ nsCOMPtr<nsISerialEventTarget> mThread;
+ RefPtr<PDMFactory> mPDMFactory;
+};
+
+} // namespace mozilla
+
+#endif // include_dom_media_ipc_RemoteDecoderManagerParent_h
diff --git a/dom/media/ipc/RemoteDecoderModule.cpp b/dom/media/ipc/RemoteDecoderModule.cpp
new file mode 100644
index 0000000000..310803c6ce
--- /dev/null
+++ b/dom/media/ipc/RemoteDecoderModule.cpp
@@ -0,0 +1,76 @@
+/* -*- 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 "RemoteDecoderModule.h"
+
+#ifdef MOZ_AV1
+# include "AOMDecoder.h"
+#endif
+#include "OpusDecoder.h"
+#include "RemoteAudioDecoder.h"
+#include "RemoteDecoderManagerChild.h"
+#include "RemoteMediaDataDecoder.h"
+#include "RemoteVideoDecoder.h"
+#include "VideoUtils.h"
+#include "VorbisDecoder.h"
+#include "WAVDecoder.h"
+#include "gfxConfig.h"
+
+namespace mozilla {
+
+using namespace ipc;
+using namespace layers;
+
+already_AddRefed<PlatformDecoderModule> RemoteDecoderModule::Create(
+ RemoteDecodeIn aLocation) {
+ MOZ_ASSERT(!XRE_IsGPUProcess() && !XRE_IsRDDProcess(),
+ "Should not be created in GPU or RDD process.");
+ if (!XRE_IsContentProcess()) {
+ // For now, the RemoteDecoderModule is only available in the content
+ // process.
+ return nullptr;
+ }
+ return MakeAndAddRef<RemoteDecoderModule>(aLocation);
+}
+
+RemoteDecoderModule::RemoteDecoderModule(RemoteDecodeIn aLocation)
+ : mLocation(aLocation) {}
+
+bool RemoteDecoderModule::SupportsMimeType(
+ const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const {
+ MOZ_CRASH("Deprecated: Use RemoteDecoderModule::Supports");
+} // namespace mozilla
+
+bool RemoteDecoderModule::Supports(
+ const SupportDecoderParams& aParams,
+ DecoderDoctorDiagnostics* aDiagnostics) const {
+ bool supports =
+ RemoteDecoderManagerChild::Supports(mLocation, aParams, aDiagnostics);
+ MOZ_LOG(sPDMLog, LogLevel::Debug,
+ ("Sandbox %s decoder %s requested type",
+ mLocation == RemoteDecodeIn::GpuProcess ? "GPU" : "RDD",
+ supports ? "supports" : "rejects"));
+ return supports;
+}
+
+RefPtr<RemoteDecoderModule::CreateDecoderPromise>
+RemoteDecoderModule::AsyncCreateDecoder(const CreateDecoderParams& aParams) {
+ if (aParams.mConfig.IsAudio()) {
+ // OpusDataDecoder will check this option to provide the same info
+ // that IsDefaultPlaybackDeviceMono provides. We want to avoid calls
+ // to IsDefaultPlaybackDeviceMono on RDD because initializing audio
+ // backends on RDD will be blocked by the sandbox.
+ if (OpusDataDecoder::IsOpus(aParams.mConfig.mMimeType) &&
+ IsDefaultPlaybackDeviceMono()) {
+ CreateDecoderParams params = aParams;
+ params.mOptions += CreateDecoderParams::Option::DefaultPlaybackDeviceMono;
+ return RemoteDecoderManagerChild::CreateAudioDecoder(params);
+ }
+ return RemoteDecoderManagerChild::CreateAudioDecoder(aParams);
+ }
+ return RemoteDecoderManagerChild::CreateVideoDecoder(aParams, mLocation);
+}
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RemoteDecoderModule.h b/dom/media/ipc/RemoteDecoderModule.h
new file mode 100644
index 0000000000..2b84f7e8c9
--- /dev/null
+++ b/dom/media/ipc/RemoteDecoderModule.h
@@ -0,0 +1,50 @@
+/* -*- 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/. */
+#ifndef include_dom_media_ipc_RemoteDecoderModule_h
+#define include_dom_media_ipc_RemoteDecoderModule_h
+#include "PlatformDecoderModule.h"
+
+namespace mozilla {
+
+enum class RemoteDecodeIn;
+
+// A decoder module that proxies decoding to either GPU or RDD process.
+class RemoteDecoderModule : public PlatformDecoderModule {
+ template <typename T, typename... Args>
+ friend already_AddRefed<T> MakeAndAddRef(Args&&...);
+
+ public:
+ static already_AddRefed<PlatformDecoderModule> Create(
+ RemoteDecodeIn aLocation);
+
+ bool SupportsMimeType(const nsACString& aMimeType,
+ DecoderDoctorDiagnostics* aDiagnostics) const override;
+
+ bool Supports(const SupportDecoderParams& aParams,
+ DecoderDoctorDiagnostics* aDiagnostics) const override;
+
+ RefPtr<CreateDecoderPromise> AsyncCreateDecoder(
+ const CreateDecoderParams& aParams) override;
+
+ already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
+ const CreateDecoderParams& aParams) override {
+ MOZ_CRASH("Not available");
+ }
+
+ already_AddRefed<MediaDataDecoder> CreateAudioDecoder(
+ const CreateDecoderParams& aParams) override {
+ MOZ_CRASH("Not available");
+ }
+
+ private:
+ explicit RemoteDecoderModule(RemoteDecodeIn aLocation);
+
+ const RemoteDecodeIn mLocation;
+};
+
+} // namespace mozilla
+
+#endif // include_dom_media_ipc_RemoteDecoderModule_h
diff --git a/dom/media/ipc/RemoteDecoderParent.cpp b/dom/media/ipc/RemoteDecoderParent.cpp
new file mode 100644
index 0000000000..a429201157
--- /dev/null
+++ b/dom/media/ipc/RemoteDecoderParent.cpp
@@ -0,0 +1,228 @@
+/* -*- 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 "RemoteDecoderParent.h"
+
+#include "RemoteDecoderManagerParent.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+
+RemoteDecoderParent::RemoteDecoderParent(
+ RemoteDecoderManagerParent* aParent,
+ const CreateDecoderParams::OptionSet& aOptions,
+ nsISerialEventTarget* aManagerThread, TaskQueue* aDecodeTaskQueue)
+ : ShmemRecycleAllocator(this),
+ mParent(aParent),
+ mOptions(aOptions),
+ mDecodeTaskQueue(aDecodeTaskQueue),
+ mManagerThread(aManagerThread) {
+ MOZ_COUNT_CTOR(RemoteDecoderParent);
+ MOZ_ASSERT(OnManagerThread());
+ // We hold a reference to ourselves to keep us alive until IPDL
+ // explictly destroys us. There may still be refs held by
+ // tasks, but no new ones should be added after we're
+ // destroyed.
+ mIPDLSelfRef = this;
+}
+
+RemoteDecoderParent::~RemoteDecoderParent() {
+ MOZ_COUNT_DTOR(RemoteDecoderParent);
+}
+
+void RemoteDecoderParent::Destroy() {
+ MOZ_ASSERT(OnManagerThread());
+ mIPDLSelfRef = nullptr;
+}
+
+mozilla::ipc::IPCResult RemoteDecoderParent::RecvInit(
+ InitResolver&& aResolver) {
+ MOZ_ASSERT(OnManagerThread());
+ RefPtr<RemoteDecoderParent> self = this;
+ mDecoder->Init()->Then(
+ mManagerThread, __func__,
+ [self, resolver = std::move(aResolver)](
+ MediaDataDecoder::InitPromise::ResolveOrRejectValue&& aValue) {
+ if (!self->CanRecv()) {
+ // The promise to the child would have already been rejected.
+ return;
+ }
+ if (aValue.IsReject()) {
+ resolver(aValue.RejectValue());
+ return;
+ }
+ auto track = aValue.ResolveValue();
+ MOZ_ASSERT(track == TrackInfo::kAudioTrack ||
+ track == TrackInfo::kVideoTrack);
+ if (self->mDecoder) {
+ nsCString hardwareReason;
+ bool hardwareAccelerated =
+ self->mDecoder->IsHardwareAccelerated(hardwareReason);
+ resolver(InitCompletionIPDL{
+ track, self->mDecoder->GetDescriptionName(), hardwareAccelerated,
+ hardwareReason, self->mDecoder->NeedsConversion()});
+ }
+ });
+ return IPC_OK();
+}
+
+void RemoteDecoderParent::DecodeNextSample(
+ const RefPtr<ArrayOfRemoteMediaRawData>& aData, size_t aIndex,
+ MediaDataDecoder::DecodedData&& aOutput, DecodeResolver&& aResolver) {
+ MOZ_ASSERT(OnManagerThread());
+
+ if (!CanRecv()) {
+ // Avoid unnecessarily creating shmem objects later.
+ return;
+ }
+
+ if (!mDecoder) {
+ // We got shutdown or the child got destroyed.
+ aResolver(MediaResult(NS_ERROR_ABORT, __func__));
+ return;
+ }
+
+ if (aData->Count() == aIndex) {
+ DecodedOutputIPDL result;
+ MediaResult rv = ProcessDecodedData(std::move(aOutput), result);
+ if (NS_FAILED(rv)) {
+ aResolver(std::move(rv)); // Out of Memory.
+ } else {
+ aResolver(std::move(result));
+ }
+ return;
+ }
+
+ RefPtr<MediaRawData> rawData = aData->ElementAt(aIndex);
+ if (!rawData) {
+ // OOM
+ aResolver(MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__));
+ return;
+ }
+
+ mDecoder->Decode(rawData)->Then(
+ mManagerThread, __func__,
+ [self = RefPtr{this}, this, aData, aIndex, output = std::move(aOutput),
+ resolver = std::move(aResolver)](
+ MediaDataDecoder::DecodePromise::ResolveOrRejectValue&&
+ aValue) mutable {
+ if (aValue.IsReject()) {
+ resolver(aValue.RejectValue());
+ return;
+ }
+
+ output.AppendElements(std::move(aValue.ResolveValue()));
+
+ // Call again in case we have more data to decode.
+ DecodeNextSample(aData, aIndex + 1, std::move(output),
+ std::move(resolver));
+ });
+}
+
+mozilla::ipc::IPCResult RemoteDecoderParent::RecvDecode(
+ ArrayOfRemoteMediaRawData* aData, DecodeResolver&& aResolver) {
+ MOZ_ASSERT(OnManagerThread());
+ // XXX: This copies the data into a buffer owned by the MediaRawData. Ideally
+ // we'd just take ownership of the shmem.
+ // Use the passed bufferSize in MediaRawDataIPDL since we can get a Shmem
+ // buffer from ShmemPool larger than the requested size.
+
+ // If we are here, we know all previously returned DecodedOutputIPDL got
+ // used by the child. We can mark all previously sent ShmemBuffer as
+ // available again.
+ ReleaseAllBuffers();
+ MediaDataDecoder::DecodedData output;
+ DecodeNextSample(aData, 0, std::move(output), std::move(aResolver));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RemoteDecoderParent::RecvFlush(
+ FlushResolver&& aResolver) {
+ MOZ_ASSERT(OnManagerThread());
+ RefPtr<RemoteDecoderParent> self = this;
+ mDecoder->Flush()->Then(
+ mManagerThread, __func__,
+ [self, resolver = std::move(aResolver)](
+ MediaDataDecoder::FlushPromise::ResolveOrRejectValue&& aValue) {
+ self->ReleaseAllBuffers();
+ if (aValue.IsReject()) {
+ resolver(aValue.RejectValue());
+ } else {
+ resolver(MediaResult(NS_OK));
+ }
+ });
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RemoteDecoderParent::RecvDrain(
+ DrainResolver&& aResolver) {
+ MOZ_ASSERT(OnManagerThread());
+ RefPtr<RemoteDecoderParent> self = this;
+ mDecoder->Drain()->Then(
+ mManagerThread, __func__,
+ [self, this, resolver = std::move(aResolver)](
+ MediaDataDecoder::DecodePromise::ResolveOrRejectValue&& aValue) {
+ ReleaseAllBuffers();
+ if (!self->CanRecv()) {
+ // Avoid unnecessarily creating shmem objects later.
+ return;
+ }
+ if (aValue.IsReject()) {
+ resolver(aValue.RejectValue());
+ return;
+ }
+ DecodedOutputIPDL output;
+ MediaResult rv =
+ ProcessDecodedData(std::move(aValue.ResolveValue()), output);
+ if (NS_FAILED(rv)) {
+ resolver(rv);
+ } else {
+ resolver(std::move(output));
+ }
+ });
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RemoteDecoderParent::RecvShutdown(
+ ShutdownResolver&& aResolver) {
+ MOZ_ASSERT(OnManagerThread());
+ if (mDecoder) {
+ RefPtr<RemoteDecoderParent> self = this;
+ mDecoder->Shutdown()->Then(
+ mManagerThread, __func__,
+ [self, resolver = std::move(aResolver)](
+ const ShutdownPromise::ResolveOrRejectValue& aValue) {
+ MOZ_ASSERT(aValue.IsResolve());
+ self->ReleaseAllBuffers();
+ resolver(true);
+ });
+ }
+ mDecoder = nullptr;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult RemoteDecoderParent::RecvSetSeekThreshold(
+ const TimeUnit& aTime) {
+ MOZ_ASSERT(OnManagerThread());
+ mDecoder->SetSeekThreshold(aTime);
+ return IPC_OK();
+}
+
+void RemoteDecoderParent::ActorDestroy(ActorDestroyReason aWhy) {
+ MOZ_ASSERT(OnManagerThread());
+ if (mDecoder) {
+ mDecoder->Shutdown();
+ mDecoder = nullptr;
+ }
+ CleanupShmemRecycleAllocator();
+}
+
+bool RemoteDecoderParent::OnManagerThread() {
+ return mParent->OnManagerThread();
+}
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RemoteDecoderParent.h b/dom/media/ipc/RemoteDecoderParent.h
new file mode 100644
index 0000000000..bce56ee77e
--- /dev/null
+++ b/dom/media/ipc/RemoteDecoderParent.h
@@ -0,0 +1,68 @@
+/* -*- 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/. */
+#ifndef include_dom_media_ipc_RemoteDecoderParent_h
+#define include_dom_media_ipc_RemoteDecoderParent_h
+
+#include "mozilla/PRemoteDecoderParent.h"
+#include "mozilla/ShmemRecycleAllocator.h"
+
+namespace mozilla {
+
+class RemoteDecoderManagerParent;
+using mozilla::ipc::IPCResult;
+
+class RemoteDecoderParent : public ShmemRecycleAllocator<RemoteDecoderParent>,
+ public PRemoteDecoderParent {
+ friend class PRemoteDecoderParent;
+
+ public:
+ // We refcount this class since the task queue can have runnables
+ // that reference us.
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteDecoderParent)
+
+ RemoteDecoderParent(RemoteDecoderManagerParent* aParent,
+ const CreateDecoderParams::OptionSet& aOptions,
+ nsISerialEventTarget* aManagerThread,
+ TaskQueue* aDecodeTaskQueue);
+
+ void Destroy();
+
+ // PRemoteDecoderParent
+ virtual IPCResult RecvConstruct(ConstructResolver&& aResolver) = 0;
+ IPCResult RecvInit(InitResolver&& aResolver);
+ IPCResult RecvDecode(ArrayOfRemoteMediaRawData* aData,
+ DecodeResolver&& aResolver);
+ IPCResult RecvFlush(FlushResolver&& aResolver);
+ IPCResult RecvDrain(DrainResolver&& aResolver);
+ IPCResult RecvShutdown(ShutdownResolver&& aResolver);
+ IPCResult RecvSetSeekThreshold(const media::TimeUnit& aTime);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ protected:
+ virtual ~RemoteDecoderParent();
+
+ bool OnManagerThread();
+
+ virtual MediaResult ProcessDecodedData(MediaDataDecoder::DecodedData&& aData,
+ DecodedOutputIPDL& aDecodedData) = 0;
+
+ const RefPtr<RemoteDecoderManagerParent> mParent;
+ const CreateDecoderParams::OptionSet mOptions;
+ const RefPtr<TaskQueue> mDecodeTaskQueue;
+ RefPtr<MediaDataDecoder> mDecoder;
+
+ private:
+ void DecodeNextSample(const RefPtr<ArrayOfRemoteMediaRawData>& aData,
+ size_t aIndex, MediaDataDecoder::DecodedData&& aOutput,
+ DecodeResolver&& aResolver);
+ RefPtr<RemoteDecoderParent> mIPDLSelfRef;
+ const RefPtr<nsISerialEventTarget> mManagerThread;
+};
+
+} // namespace mozilla
+
+#endif // include_dom_media_ipc_RemoteDecoderParent_h
diff --git a/dom/media/ipc/RemoteImageHolder.cpp b/dom/media/ipc/RemoteImageHolder.cpp
new file mode 100644
index 0000000000..31b28ad0ce
--- /dev/null
+++ b/dom/media/ipc/RemoteImageHolder.cpp
@@ -0,0 +1,166 @@
+/* -*- 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 "RemoteImageHolder.h"
+
+#include "GPUVideoImage.h"
+#include "mozilla/PRemoteDecoderChild.h"
+#include "mozilla/RemoteDecoderManagerChild.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/VideoBridgeUtils.h"
+
+namespace mozilla {
+
+using namespace gfx;
+using namespace layers;
+
+RemoteImageHolder::RemoteImageHolder() = default;
+RemoteImageHolder::RemoteImageHolder(layers::IGPUVideoSurfaceManager* aManager,
+ layers::VideoBridgeSource aSource,
+ const gfx::IntSize& aSize,
+ const layers::SurfaceDescriptor& aSD)
+ : mSource(aSource), mSize(aSize), mSD(Some(aSD)), mManager(aManager) {}
+RemoteImageHolder::RemoteImageHolder(RemoteImageHolder&& aOther)
+ : mSource(aOther.mSource),
+ mSize(aOther.mSize),
+ mSD(std::move(aOther.mSD)),
+ mManager(aOther.mManager) {
+ aOther.mSD = Nothing();
+}
+
+already_AddRefed<Image> RemoteImageHolder::DeserializeImage(
+ layers::BufferRecycleBin* aBufferRecycleBin) {
+ MOZ_ASSERT(mSD && mSD->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer);
+ const SurfaceDescriptorBuffer& sdBuffer = mSD->get_SurfaceDescriptorBuffer();
+ MOZ_ASSERT(sdBuffer.desc().type() == BufferDescriptor::TYCbCrDescriptor);
+ if (sdBuffer.desc().type() != BufferDescriptor::TYCbCrDescriptor ||
+ !aBufferRecycleBin) {
+ return nullptr;
+ }
+ const YCbCrDescriptor& descriptor = sdBuffer.desc().get_YCbCrDescriptor();
+
+ uint8_t* buffer = nullptr;
+ const MemoryOrShmem& memOrShmem = sdBuffer.data();
+ switch (memOrShmem.type()) {
+ case MemoryOrShmem::Tuintptr_t:
+ buffer = reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
+ break;
+ case MemoryOrShmem::TShmem:
+ buffer = memOrShmem.get_Shmem().get<uint8_t>();
+ break;
+ default:
+ MOZ_ASSERT(false, "Unknown MemoryOrShmem type");
+ }
+ if (!buffer) {
+ return nullptr;
+ }
+
+ PlanarYCbCrData pData;
+ pData.mYSize = descriptor.ySize();
+ pData.mYStride = descriptor.yStride();
+ pData.mCbCrSize = descriptor.cbCrSize();
+ pData.mCbCrStride = descriptor.cbCrStride();
+ // default mYSkip, mCbSkip, mCrSkip because not held in YCbCrDescriptor
+ pData.mYSkip = pData.mCbSkip = pData.mCrSkip = 0;
+ gfx::IntRect display = descriptor.display();
+ pData.mPicX = display.X();
+ pData.mPicY = display.Y();
+ pData.mPicSize = display.Size();
+ pData.mStereoMode = descriptor.stereoMode();
+ pData.mColorDepth = descriptor.colorDepth();
+ pData.mYUVColorSpace = descriptor.yUVColorSpace();
+ pData.mYChannel = ImageDataSerializer::GetYChannel(buffer, descriptor);
+ pData.mCbChannel = ImageDataSerializer::GetCbChannel(buffer, descriptor);
+ pData.mCrChannel = ImageDataSerializer::GetCrChannel(buffer, descriptor);
+
+ // images coming from AOMDecoder are RecyclingPlanarYCbCrImages.
+ RefPtr<RecyclingPlanarYCbCrImage> image =
+ new RecyclingPlanarYCbCrImage(aBufferRecycleBin);
+ bool setData = image->CopyData(pData);
+ MOZ_ASSERT(setData);
+
+ switch (memOrShmem.type()) {
+ case MemoryOrShmem::Tuintptr_t:
+ delete[] reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
+ break;
+ case MemoryOrShmem::TShmem:
+ // Memory buffer will be recycled by the parent automatically.
+ break;
+ default:
+ MOZ_ASSERT(false, "Unknown MemoryOrShmem type");
+ }
+
+ if (!setData) {
+ return nullptr;
+ }
+
+ return image.forget();
+}
+
+already_AddRefed<layers::Image> RemoteImageHolder::TransferToImage(
+ layers::BufferRecycleBin* aBufferRecycleBin) {
+ if (IsEmpty()) {
+ return nullptr;
+ }
+ RefPtr<Image> image;
+ if (mSD->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer) {
+ image = DeserializeImage(aBufferRecycleBin);
+ } else {
+ // The Image here creates a TextureData object that takes ownership
+ // of the SurfaceDescriptor, and is responsible for making sure that
+ // it gets deallocated.
+ SurfaceDescriptorRemoteDecoder remoteSD =
+ static_cast<const SurfaceDescriptorGPUVideo&>(*mSD);
+ remoteSD.source() = Some(mSource);
+ image = new GPUVideoImage(mManager, remoteSD, mSize);
+ }
+ mSD = Nothing();
+ mManager = nullptr;
+
+ return image.forget();
+}
+
+RemoteImageHolder::~RemoteImageHolder() {
+ // GPU Images are held by the RemoteDecoderManagerParent, we didn't get to use
+ // this image holder (the decoder could have been flushed). We don't need to
+ // worry about Shmem based image as the Shmem will be automatically re-used
+ // once the decoder is used again.
+ if (!IsEmpty() && mManager &&
+ mSD->type() != SurfaceDescriptor::TSurfaceDescriptorBuffer) {
+ SurfaceDescriptorRemoteDecoder remoteSD =
+ static_cast<const SurfaceDescriptorGPUVideo&>(*mSD);
+ mManager->DeallocateSurfaceDescriptor(remoteSD);
+ }
+}
+
+/* static */ void ipc::IPDLParamTraits<RemoteImageHolder>::Write(
+ IPC::Message* aMsg, ipc::IProtocol* aActor, RemoteImageHolder&& aParam) {
+ WriteIPDLParam(aMsg, aActor, aParam.mSource);
+ WriteIPDLParam(aMsg, aActor, aParam.mSize);
+ WriteIPDLParam(aMsg, aActor, aParam.mSD);
+ // Empty this holder.
+ aParam.mSD = Nothing();
+ aParam.mManager = nullptr;
+}
+
+/* static */ bool ipc::IPDLParamTraits<RemoteImageHolder>::Read(
+ const IPC::Message* aMsg, PickleIterator* aIter, ipc::IProtocol* aActor,
+ RemoteImageHolder* aResult) {
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSource) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSize) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSD)) {
+ return false;
+ }
+ if (!aResult->IsEmpty()) {
+ aResult->mManager = RemoteDecoderManagerChild::GetSingleton(
+ aResult->mSource == VideoBridgeSource::GpuProcess
+ ? RemoteDecodeIn::GpuProcess
+ : RemoteDecodeIn::RddProcess);
+ }
+ return true;
+}
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RemoteImageHolder.h b/dom/media/ipc/RemoteImageHolder.h
new file mode 100644
index 0000000000..c677b388ad
--- /dev/null
+++ b/dom/media/ipc/RemoteImageHolder.h
@@ -0,0 +1,64 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_media_RemoteImageHolder_h
+#define mozilla_dom_media_RemoteImageHolder_h
+
+#include "MediaData.h"
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/layers/VideoBridgeUtils.h"
+
+namespace mozilla {
+namespace layers {
+class BufferRecycleBin;
+class IGPUVideoSurfaceManager;
+class SurfaceDescriptor;
+} // namespace layers
+class RemoteImageHolder final {
+ friend struct ipc::IPDLParamTraits<RemoteImageHolder>;
+
+ public:
+ RemoteImageHolder();
+ RemoteImageHolder(layers::IGPUVideoSurfaceManager* aManager,
+ layers::VideoBridgeSource aSource,
+ const gfx::IntSize& aSize,
+ const layers::SurfaceDescriptor& aSD);
+ RemoteImageHolder(RemoteImageHolder&& aOther);
+ // Ensure we never copy this object.
+ RemoteImageHolder(const RemoteImageHolder& aOther) = delete;
+ RemoteImageHolder& operator=(const RemoteImageHolder& aOther) = delete;
+ ~RemoteImageHolder();
+
+ bool IsEmpty() const { return mSD.isNothing(); }
+ // Move content of RemoteImageHolder into a usable Image. Ownership is
+ // transfered to that Image.
+ already_AddRefed<layers::Image> TransferToImage(
+ layers::BufferRecycleBin* aBufferRecycleBin = nullptr);
+
+ private:
+ already_AddRefed<layers::Image> DeserializeImage(
+ layers::BufferRecycleBin* aBufferRecycleBin);
+ // We need a default for the default constructor, never used in practice.
+ layers::VideoBridgeSource mSource = layers::VideoBridgeSource::GpuProcess;
+ gfx::IntSize mSize;
+ Maybe<layers::SurfaceDescriptor> mSD;
+ RefPtr<layers::IGPUVideoSurfaceManager> mManager;
+};
+
+template <>
+struct ipc::IPDLParamTraits<RemoteImageHolder> {
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ RemoteImageHolder&& aParam);
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, RemoteImageHolder* aResult);
+};
+
+} // namespace mozilla
+
+#endif // mozilla_dom_media_RemoteImageHolder_h
diff --git a/dom/media/ipc/RemoteMediaData.cpp b/dom/media/ipc/RemoteMediaData.cpp
new file mode 100644
index 0000000000..1411cae2a2
--- /dev/null
+++ b/dom/media/ipc/RemoteMediaData.cpp
@@ -0,0 +1,337 @@
+/* -*- 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 "RemoteMediaData.h"
+
+#include "mozilla/CheckedInt.h"
+#include "mozilla/dom/MediaIPCUtils.h"
+#include "mozilla/ipc/Shmem.h"
+
+namespace mozilla {
+
+bool RemoteArrayOfByteBuffer::AllocateShmem(
+ size_t aSize, std::function<ShmemBuffer(size_t)>& aAllocator) {
+ ShmemBuffer buffer = aAllocator(aSize);
+ if (!buffer.Valid()) {
+ return false;
+ }
+ mBuffers.emplace(std::move(buffer.Get()));
+ return true;
+}
+
+uint8_t* RemoteArrayOfByteBuffer::BuffersStartAddress() const {
+ MOZ_ASSERT(mBuffers);
+ return mBuffers->get<uint8_t>();
+}
+
+bool RemoteArrayOfByteBuffer::Check(size_t aOffset, size_t aSizeInBytes) const {
+ return mBuffers && mBuffers->IsReadable() &&
+ detail::IsAddValid(aOffset, aSizeInBytes) &&
+ aOffset + aSizeInBytes <= mBuffers->Size<uint8_t>();
+}
+
+void RemoteArrayOfByteBuffer::Write(size_t aOffset, const void* aSourceAddr,
+ size_t aSizeInBytes) {
+ if (!aSizeInBytes) {
+ return;
+ }
+ MOZ_DIAGNOSTIC_ASSERT(Check(aOffset, aSizeInBytes),
+ "Allocated Shmem is too small");
+ memcpy(BuffersStartAddress() + aOffset, aSourceAddr, aSizeInBytes);
+}
+
+RemoteArrayOfByteBuffer::RemoteArrayOfByteBuffer() = default;
+
+RemoteArrayOfByteBuffer::RemoteArrayOfByteBuffer(
+ const nsTArray<RefPtr<MediaByteBuffer>>& aArray,
+ std::function<ShmemBuffer(size_t)>& aAllocator) {
+ // Determine the total size we will need for this object.
+ size_t totalSize = 0;
+ for (const auto& buffer : aArray) {
+ if (buffer) {
+ totalSize += buffer->Length();
+ }
+ }
+ if (totalSize) {
+ if (!AllocateShmem(totalSize, aAllocator)) {
+ return;
+ }
+ }
+ size_t offset = 0;
+ for (const auto& buffer : aArray) {
+ size_t sizeBuffer = buffer ? buffer->Length() : 0;
+ if (totalSize && sizeBuffer) {
+ Write(offset, buffer->Elements(), sizeBuffer);
+ }
+ mOffsets.AppendElement(OffsetEntry{offset, sizeBuffer});
+ offset += sizeBuffer;
+ }
+ mIsValid = true;
+}
+
+RemoteArrayOfByteBuffer& RemoteArrayOfByteBuffer::operator=(
+ RemoteArrayOfByteBuffer&& aOther) noexcept {
+ mIsValid = aOther.mIsValid;
+ mBuffers = std::move(aOther.mBuffers);
+ mOffsets = std::move(aOther.mOffsets);
+ aOther.mIsValid = false;
+ return *this;
+}
+
+RemoteArrayOfByteBuffer::~RemoteArrayOfByteBuffer() = default;
+
+already_AddRefed<MediaByteBuffer> RemoteArrayOfByteBuffer::MediaByteBufferAt(
+ size_t aIndex) const {
+ MOZ_ASSERT(aIndex < Count());
+ const OffsetEntry& entry = mOffsets[aIndex];
+ if (!mBuffers || !Get<1>(entry)) {
+ // It's an empty one.
+ return nullptr;
+ }
+ size_t entrySize = Get<1>(entry);
+ if (!Check(Get<0>(entry), entrySize)) {
+ // This Shmem is corrupted and can't contain the data we are about to
+ // retrieve. We return an empty array instead of asserting to allow for
+ // recovery.
+ return nullptr;
+ }
+ RefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(entrySize);
+ buffer->SetLength(entrySize);
+ memcpy(buffer->Elements(), mBuffers->get<uint8_t>() + Get<0>(entry),
+ entrySize);
+ return buffer.forget();
+}
+
+/*static */ void ipc::IPDLParamTraits<RemoteArrayOfByteBuffer>::Write(
+ IPC::Message* aMsg, ipc::IProtocol* aActor,
+ const RemoteArrayOfByteBuffer& aVar) {
+ WriteIPDLParam(aMsg, aActor, aVar.mIsValid);
+ // We need the following gymnastic as the Shmem transfered over IPC will be
+ // revoked. We must create a temporary one instead so that it can be recycled
+ // later back into the original ShmemPool.
+ if (aVar.mBuffers) {
+ WriteIPDLParam(aMsg, aActor, Some(ipc::Shmem(*aVar.mBuffers)));
+ } else {
+ WriteIPDLParam(aMsg, aActor, Maybe<ipc::Shmem>());
+ }
+ WriteIPDLParam(aMsg, aActor, aVar.mOffsets);
+}
+
+/* static */ bool ipc::IPDLParamTraits<RemoteArrayOfByteBuffer>::Read(
+ const IPC::Message* aMsg, PickleIterator* aIter,
+ mozilla::ipc::IProtocol* aActor, RemoteArrayOfByteBuffer* aVar) {
+ return ReadIPDLParam(aMsg, aIter, aActor, &aVar->mIsValid) &&
+ ReadIPDLParam(aMsg, aIter, aActor, &aVar->mBuffers) &&
+ ReadIPDLParam(aMsg, aIter, aActor, &aVar->mOffsets);
+}
+
+bool ArrayOfRemoteMediaRawData::Fill(
+ const nsTArray<RefPtr<MediaRawData>>& aData,
+ std::function<ShmemBuffer(size_t)>&& aAllocator) {
+ nsTArray<AlignedByteBuffer> dataBuffers(aData.Length());
+ nsTArray<AlignedByteBuffer> alphaBuffers(aData.Length());
+ nsTArray<RefPtr<MediaByteBuffer>> extraDataBuffers(aData.Length());
+ for (auto&& entry : aData) {
+ dataBuffers.AppendElement(std::move(entry->mBuffer));
+ alphaBuffers.AppendElement(std::move(entry->mAlphaBuffer));
+ extraDataBuffers.AppendElement(std::move(entry->mExtraData));
+ mSamples.AppendElement(RemoteMediaRawData{
+ MediaDataIPDL(entry->mOffset, entry->mTime, entry->mTimecode,
+ entry->mDuration, entry->mKeyframe),
+ entry->mEOS, entry->mDiscardPadding,
+ entry->mOriginalPresentationWindow});
+ }
+ mBuffers = RemoteArrayOfByteBuffer(dataBuffers, aAllocator);
+ if (!mBuffers.IsValid()) {
+ return false;
+ }
+ mAlphaBuffers = RemoteArrayOfByteBuffer(alphaBuffers, aAllocator);
+ if (!mAlphaBuffers.IsValid()) {
+ return false;
+ }
+ mExtraDatas = RemoteArrayOfByteBuffer(extraDataBuffers, aAllocator);
+ return mExtraDatas.IsValid();
+}
+
+already_AddRefed<MediaRawData> ArrayOfRemoteMediaRawData::ElementAt(
+ size_t aIndex) const {
+ if (!IsValid()) {
+ return nullptr;
+ }
+ MOZ_ASSERT(aIndex < Count());
+ MOZ_DIAGNOSTIC_ASSERT(mBuffers.Count() == Count() &&
+ mAlphaBuffers.Count() == Count() &&
+ mExtraDatas.Count() == Count(),
+ "Something ain't right here");
+ const auto& sample = mSamples[aIndex];
+ AlignedByteBuffer data = mBuffers.AlignedBufferAt<uint8_t>(aIndex);
+ if (mBuffers.SizeAt(aIndex) && !data) {
+ // OOM
+ return nullptr;
+ }
+ AlignedByteBuffer alphaData = mAlphaBuffers.AlignedBufferAt<uint8_t>(aIndex);
+ if (mAlphaBuffers.SizeAt(aIndex) && !alphaData) {
+ // OOM
+ return nullptr;
+ }
+ RefPtr<MediaRawData> rawData;
+ if (mAlphaBuffers.SizeAt(aIndex)) {
+ rawData = new MediaRawData(std::move(data), std::move(alphaData));
+ } else {
+ rawData = new MediaRawData(std::move(data));
+ }
+ rawData->mOffset = sample.mBase.offset();
+ rawData->mTime = sample.mBase.time();
+ rawData->mTimecode = sample.mBase.timecode();
+ rawData->mDuration = sample.mBase.duration();
+ rawData->mKeyframe = sample.mBase.keyframe();
+ rawData->mEOS = sample.mEOS;
+ rawData->mDiscardPadding = sample.mDiscardPadding;
+ rawData->mExtraData = mExtraDatas.MediaByteBufferAt(aIndex);
+ return rawData.forget();
+}
+
+/*static */ void ipc::IPDLParamTraits<ArrayOfRemoteMediaRawData*>::Write(
+ IPC::Message* aMsg, ipc::IProtocol* aActor,
+ ArrayOfRemoteMediaRawData* aVar) {
+ WriteIPDLParam(aMsg, aActor, std::move(aVar->mSamples));
+ WriteIPDLParam(aMsg, aActor, std::move(aVar->mBuffers));
+ WriteIPDLParam(aMsg, aActor, std::move(aVar->mAlphaBuffers));
+ WriteIPDLParam(aMsg, aActor, std::move(aVar->mExtraDatas));
+}
+
+/* static */ bool ipc::IPDLParamTraits<ArrayOfRemoteMediaRawData*>::Read(
+ const IPC::Message* aMsg, PickleIterator* aIter,
+ mozilla::ipc::IProtocol* aActor, RefPtr<ArrayOfRemoteMediaRawData>* aVar) {
+ auto array = MakeRefPtr<ArrayOfRemoteMediaRawData>();
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &array->mSamples) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &array->mBuffers) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &array->mAlphaBuffers) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &array->mExtraDatas)) {
+ return false;
+ }
+ *aVar = std::move(array);
+ return true;
+}
+
+/* static */ void
+ipc::IPDLParamTraits<ArrayOfRemoteMediaRawData::RemoteMediaRawData>::Write(
+ IPC::Message* aMsg, ipc::IProtocol* aActor, const paramType& aVar) {
+ WriteIPDLParam(aMsg, aActor, aVar.mBase);
+ WriteIPDLParam(aMsg, aActor, aVar.mEOS);
+ WriteIPDLParam(aMsg, aActor, aVar.mDiscardPadding);
+ WriteIPDLParam(aMsg, aActor, aVar.mOriginalPresentationWindow);
+}
+
+/* static */ bool
+ipc::IPDLParamTraits<ArrayOfRemoteMediaRawData::RemoteMediaRawData>::Read(
+ const IPC::Message* aMsg, PickleIterator* aIter, ipc::IProtocol* aActor,
+ paramType* aVar) {
+ MediaDataIPDL mBase;
+ return ReadIPDLParam(aMsg, aIter, aActor, &aVar->mBase) &&
+ ReadIPDLParam(aMsg, aIter, aActor, &aVar->mEOS) &&
+ ReadIPDLParam(aMsg, aIter, aActor, &aVar->mDiscardPadding) &&
+ ReadIPDLParam(aMsg, aIter, aActor, &aVar->mOriginalPresentationWindow);
+};
+
+bool ArrayOfRemoteAudioData::Fill(
+ const nsTArray<RefPtr<AudioData>>& aData,
+ std::function<ShmemBuffer(size_t)>&& aAllocator) {
+ nsTArray<AlignedAudioBuffer> dataBuffers(aData.Length());
+ for (auto&& entry : aData) {
+ dataBuffers.AppendElement(std::move(entry->mAudioData));
+ mSamples.AppendElement(RemoteAudioData{
+ MediaDataIPDL(entry->mOffset, entry->mTime, entry->mTimecode,
+ entry->mDuration, entry->mKeyframe),
+ entry->mChannels, entry->mRate, uint32_t(entry->mChannelMap),
+ entry->mOriginalTime, entry->mTrimWindow, entry->mFrames,
+ entry->mDataOffset});
+ }
+ mBuffers = RemoteArrayOfByteBuffer(dataBuffers, aAllocator);
+ if (!mBuffers.IsValid()) {
+ return false;
+ }
+ return true;
+}
+
+already_AddRefed<AudioData> ArrayOfRemoteAudioData::ElementAt(
+ size_t aIndex) const {
+ if (!IsValid()) {
+ return nullptr;
+ }
+ MOZ_ASSERT(aIndex < Count());
+ MOZ_DIAGNOSTIC_ASSERT(mBuffers.Count() == Count(),
+ "Something ain't right here");
+ const auto& sample = mSamples[aIndex];
+ AlignedAudioBuffer data = mBuffers.AlignedBufferAt<AudioDataValue>(aIndex);
+ if (mBuffers.SizeAt(aIndex) && !data) {
+ // OOM
+ return nullptr;
+ }
+ auto audioData = MakeRefPtr<AudioData>(
+ sample.mBase.offset(), sample.mBase.time(), std::move(data),
+ sample.mChannels, sample.mRate, sample.mChannelMap);
+ // An AudioData's duration is set at construction time based on the size of
+ // the provided buffer. However, if a trim window is set, this value will be
+ // incorrect. We have to re-set it to what it actually was.
+ audioData->mDuration = sample.mBase.duration();
+ audioData->mOriginalTime = sample.mOriginalTime;
+ audioData->mTrimWindow = sample.mTrimWindow;
+ audioData->mFrames = sample.mFrames;
+ audioData->mDataOffset = sample.mDataOffset;
+ return audioData.forget();
+}
+
+/*static */ void ipc::IPDLParamTraits<ArrayOfRemoteAudioData*>::Write(
+ IPC::Message* aMsg, ipc::IProtocol* aActor, ArrayOfRemoteAudioData* aVar) {
+ WriteIPDLParam(aMsg, aActor, std::move(aVar->mSamples));
+ WriteIPDLParam(aMsg, aActor, std::move(aVar->mBuffers));
+}
+
+/* static */ bool ipc::IPDLParamTraits<ArrayOfRemoteAudioData*>::Read(
+ const IPC::Message* aMsg, PickleIterator* aIter,
+ mozilla::ipc::IProtocol* aActor, RefPtr<ArrayOfRemoteAudioData>* aVar) {
+ auto array = MakeRefPtr<ArrayOfRemoteAudioData>();
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &array->mSamples) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &array->mBuffers)) {
+ return false;
+ }
+ *aVar = std::move(array);
+ return true;
+}
+
+/* static */ void
+ipc::IPDLParamTraits<ArrayOfRemoteAudioData::RemoteAudioData>::Write(
+ IPC::Message* aMsg, ipc::IProtocol* aActor, const paramType& aVar) {
+ WriteIPDLParam(aMsg, aActor, aVar.mBase);
+ WriteIPDLParam(aMsg, aActor, aVar.mChannels);
+ WriteIPDLParam(aMsg, aActor, aVar.mRate);
+ WriteIPDLParam(aMsg, aActor, aVar.mChannelMap);
+ WriteIPDLParam(aMsg, aActor, aVar.mOriginalTime);
+ WriteIPDLParam(aMsg, aActor, aVar.mTrimWindow);
+ WriteIPDLParam(aMsg, aActor, aVar.mFrames);
+ WriteIPDLParam(aMsg, aActor, aVar.mDataOffset);
+}
+
+/* static */ bool
+ipc::IPDLParamTraits<ArrayOfRemoteAudioData::RemoteAudioData>::Read(
+ const IPC::Message* aMsg, PickleIterator* aIter, ipc::IProtocol* aActor,
+ paramType* aVar) {
+ MediaDataIPDL mBase;
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &aVar->mBase) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &aVar->mChannels) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &aVar->mRate) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &aVar->mChannelMap) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &aVar->mOriginalTime) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &aVar->mTrimWindow) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &aVar->mFrames) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &aVar->mDataOffset)) {
+ return false;
+ }
+ return true;
+};
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RemoteMediaData.h b/dom/media/ipc/RemoteMediaData.h
new file mode 100644
index 0000000000..89a9a25d32
--- /dev/null
+++ b/dom/media/ipc/RemoteMediaData.h
@@ -0,0 +1,392 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_media_ipc_RemoteMediaData_h
+#define mozilla_dom_media_ipc_RemoteMediaData_h
+
+#include <functional>
+
+#include "MediaData.h"
+#include "PlatformDecoderModule.h"
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/GfxMessageUtils.h"
+#include "mozilla/PMediaDecoderParams.h"
+#include "mozilla/RemoteImageHolder.h"
+#include "mozilla/ShmemPool.h"
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+
+class ShmemPool;
+
+namespace ipc {
+class IProtocol;
+class Shmem;
+} // namespace ipc
+
+//-----------------------------------------------------------------------------
+// Declaration of the IPDL type |struct RemoteVideoData|
+//
+// We can't use the generated binding in order to use move semantics properly
+// (see bug 1664362)
+class RemoteVideoData final {
+ private:
+ typedef mozilla::gfx::IntSize IntSize;
+
+ public:
+ RemoteVideoData() = default;
+
+ RemoteVideoData(const MediaDataIPDL& aBase, const IntSize& aDisplay,
+ RemoteImageHolder&& aImage, int32_t aFrameID)
+ : mBase(aBase),
+ mDisplay(aDisplay),
+ mImage(std::move(aImage)),
+ mFrameID(aFrameID) {}
+
+ // This is equivalent to the old RemoteVideoDataIPDL object and is similar to
+ // the RemoteAudioDataIPDL object. To ensure style consistency we use the IPDL
+ // naming convention here.
+ MediaDataIPDL& base() { return mBase; }
+ const MediaDataIPDL& base() const { return mBase; }
+
+ IntSize& display() { return mDisplay; }
+ const IntSize& display() const { return mDisplay; }
+
+ RemoteImageHolder& image() { return mImage; }
+ const RemoteImageHolder& image() const { return mImage; }
+
+ int32_t& frameID() { return mFrameID; }
+ const int32_t& frameID() const { return mFrameID; }
+
+ private:
+ friend struct ipc::IPDLParamTraits<RemoteVideoData>;
+ MediaDataIPDL mBase;
+ IntSize mDisplay;
+ RemoteImageHolder mImage;
+ int32_t mFrameID;
+};
+
+// Until bug 1572054 is resolved, we can't move our objects when using IPDL's
+// union or array. They are always copied. So we make the class refcounted to
+// and always pass it by pointed to bypass the problem for now.
+class ArrayOfRemoteVideoData final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ArrayOfRemoteVideoData)
+ public:
+ ArrayOfRemoteVideoData() = default;
+ ArrayOfRemoteVideoData(ArrayOfRemoteVideoData&& aOther)
+ : mArray(std::move(aOther.mArray)) {}
+ explicit ArrayOfRemoteVideoData(nsTArray<RemoteVideoData>&& aOther)
+ : mArray(std::move(aOther)) {}
+ ArrayOfRemoteVideoData(const ArrayOfRemoteVideoData& aOther) {
+ MOZ_CRASH("Should never be used but declared by generated IPDL binding");
+ }
+ ArrayOfRemoteVideoData& operator=(ArrayOfRemoteVideoData&& aOther) noexcept {
+ if (this != &aOther) {
+ mArray = std::move(aOther.mArray);
+ }
+ return *this;
+ }
+ ArrayOfRemoteVideoData& operator=(nsTArray<RemoteVideoData>&& aOther) {
+ mArray = std::move(aOther);
+ return *this;
+ }
+
+ void AppendElements(nsTArray<RemoteVideoData>&& aOther) {
+ mArray.AppendElements(std::move(aOther));
+ }
+ void Append(RemoteVideoData&& aVideo) {
+ mArray.AppendElement(std::move(aVideo));
+ }
+ const nsTArray<RemoteVideoData>& Array() const { return mArray; }
+ nsTArray<RemoteVideoData>& Array() { return mArray; }
+
+ private:
+ ~ArrayOfRemoteVideoData() = default;
+ friend struct ipc::IPDLParamTraits<mozilla::ArrayOfRemoteVideoData*>;
+ nsTArray<RemoteVideoData> mArray;
+};
+
+/* The class will pack either an array of AlignedBuffer or MediaByteBuffer
+ * into a single Shmem objects. */
+class RemoteArrayOfByteBuffer {
+ public:
+ RemoteArrayOfByteBuffer();
+ template <typename Type>
+ RemoteArrayOfByteBuffer(const nsTArray<AlignedBuffer<Type>>& aArray,
+ std::function<ShmemBuffer(size_t)>& aAllocator) {
+ // Determine the total size we will need for this object.
+ size_t totalSize = 0;
+ for (auto& buffer : aArray) {
+ totalSize += buffer.Size();
+ }
+ if (totalSize) {
+ if (!AllocateShmem(totalSize, aAllocator)) {
+ return;
+ }
+ }
+ size_t offset = 0;
+ for (auto& buffer : aArray) {
+ if (totalSize && buffer && buffer.Size()) {
+ Write(offset, buffer.Data(), buffer.Size());
+ }
+ mOffsets.AppendElement(OffsetEntry{offset, buffer.Size()});
+ offset += buffer.Size();
+ }
+ mIsValid = true;
+ }
+
+ RemoteArrayOfByteBuffer(const nsTArray<RefPtr<MediaByteBuffer>>& aArray,
+ std::function<ShmemBuffer(size_t)>& aAllocator);
+ RemoteArrayOfByteBuffer& operator=(RemoteArrayOfByteBuffer&& aOther) noexcept;
+
+ // Return the packed aIndexth buffer as an AlignedByteBuffer.
+ // The operation is fallible should an out of memory be encountered. The
+ // result should be tested accordingly.
+ template <typename Type>
+ AlignedBuffer<Type> AlignedBufferAt(size_t aIndex) const {
+ MOZ_ASSERT(aIndex < Count());
+ const OffsetEntry& entry = mOffsets[aIndex];
+ size_t entrySize = Get<1>(entry);
+ if (!mBuffers || !entrySize) {
+ // It's an empty one.
+ return AlignedBuffer<Type>();
+ }
+ if (!Check(Get<0>(entry), entrySize)) {
+ // This Shmem is corrupted and can't contain the data we are about to
+ // retrieve. We return an empty array instead of asserting to allow for
+ // recovery.
+ return AlignedBuffer<Type>();
+ }
+ if (0 != entrySize % sizeof(Type)) {
+ // There's an error, that entry can't represent this data.
+ return AlignedBuffer<Type>();
+ }
+ return AlignedBuffer<Type>(
+ reinterpret_cast<Type*>(BuffersStartAddress() + Get<0>(entry)),
+ entrySize / sizeof(Type));
+ }
+
+ // Return the packed aIndexth buffer as aMediaByteBuffer.
+ // Will return nullptr if the packed buffer was originally empty.
+ already_AddRefed<MediaByteBuffer> MediaByteBufferAt(size_t aIndex) const;
+ // Return the size of the aIndexth buffer.
+ size_t SizeAt(size_t aIndex) const { return Get<1>(mOffsets[aIndex]); }
+ // Return false if an out of memory error was encountered during construction.
+ bool IsValid() const { return mIsValid; };
+ // Return the number of buffers packed into this entity.
+ size_t Count() const { return mOffsets.Length(); }
+ virtual ~RemoteArrayOfByteBuffer();
+
+ private:
+ friend struct ipc::IPDLParamTraits<RemoteArrayOfByteBuffer>;
+ // Allocate shmem, false if an error occurred.
+ bool AllocateShmem(size_t aSize,
+ std::function<ShmemBuffer(size_t)>& aAllocator);
+ // The starting address of the Shmem
+ uint8_t* BuffersStartAddress() const;
+ // Check that the allocated Shmem can contain such range.
+ bool Check(size_t aOffset, size_t aSizeInBytes) const;
+ void Write(size_t aOffset, const void* aSourceAddr, size_t aSizeInBytes);
+ // Set to false is the buffer isn't initialized yet or a memory error occurred
+ // during construction.
+ bool mIsValid = false;
+ // The packed data. The Maybe will be empty if all buffers packed were
+ // orignally empty.
+ Maybe<ipc::Shmem> mBuffers;
+ // The offset to the start of the individual buffer and its size (all in
+ // bytes)
+ typedef Tuple<size_t, size_t> OffsetEntry;
+ nsTArray<OffsetEntry> mOffsets;
+};
+
+/* The class will pack an array of MediaRawData using at most three Shmem
+ * objects. Under the most common scenaria, only two Shmems will be used as
+ * there are few videos with an alpha channel in the wild.
+ * We unfortunately can't populate the array at construction nor present an
+ * interface similar to an actual nsTArray or the ArrayOfRemoteVideoData above
+ * as currently IPC serialization is always non-fallible. So we must create the
+ * object first, fill it to determine if we ran out of memory and then send the
+ * object over IPC.
+ */
+class ArrayOfRemoteMediaRawData {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ArrayOfRemoteMediaRawData)
+ public:
+ // Fill the content, return false if an OOM occurred.
+ bool Fill(const nsTArray<RefPtr<MediaRawData>>& aData,
+ std::function<ShmemBuffer(size_t)>&& aAllocator);
+
+ // Return the aIndexth MediaRawData or nullptr if a memory error occurred.
+ already_AddRefed<MediaRawData> ElementAt(size_t aIndex) const;
+
+ // Return the number of MediaRawData stored in this container.
+ size_t Count() const { return mSamples.Length(); }
+ bool IsEmpty() const { return Count() == 0; }
+ bool IsValid() const {
+ return mBuffers.IsValid() && mAlphaBuffers.IsValid() &&
+ mExtraDatas.IsValid();
+ }
+
+ struct RemoteMediaRawData {
+ MediaDataIPDL mBase;
+ bool mEOS;
+ uint32_t mDiscardPadding;
+ Maybe<media::TimeInterval> mOriginalPresentationWindow;
+ };
+
+ private:
+ friend struct ipc::IPDLParamTraits<ArrayOfRemoteMediaRawData*>;
+ virtual ~ArrayOfRemoteMediaRawData() = default;
+
+ nsTArray<RemoteMediaRawData> mSamples;
+ RemoteArrayOfByteBuffer mBuffers;
+ RemoteArrayOfByteBuffer mAlphaBuffers;
+ RemoteArrayOfByteBuffer mExtraDatas;
+};
+
+/* The class will pack an array of MediaAudioData using at most a single Shmem
+ * objects.
+ * We unfortunately can't populate the array at construction nor present an
+ * interface similar to an actual nsTArray or the ArrayOfRemoteVideoData above
+ * as currently IPC serialization is always non-fallible. So we must create the
+ * object first, fill it to determine if we ran out of memory and then send the
+ * object over IPC.
+ */
+class ArrayOfRemoteAudioData final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ArrayOfRemoteAudioData)
+ public:
+ // Fill the content, return false if an OOM occurred.
+ bool Fill(const nsTArray<RefPtr<AudioData>>& aData,
+ std::function<ShmemBuffer(size_t)>&& aAllocator);
+
+ // Return the aIndexth MediaRawData or nullptr if a memory error occurred.
+ already_AddRefed<AudioData> ElementAt(size_t aIndex) const;
+
+ // Return the number of MediaRawData stored in this container.
+ size_t Count() const { return mSamples.Length(); }
+ bool IsEmpty() const { return Count() == 0; }
+ bool IsValid() const { return mBuffers.IsValid(); }
+
+ struct RemoteAudioData {
+ friend struct ipc::IPDLParamTraits<RemoteVideoData>;
+ MediaDataIPDL mBase;
+ uint32_t mChannels;
+ uint32_t mRate;
+ uint32_t mChannelMap;
+ media::TimeUnit mOriginalTime;
+ Maybe<media::TimeInterval> mTrimWindow;
+ uint32_t mFrames;
+ size_t mDataOffset;
+ };
+
+ private:
+ friend struct ipc::IPDLParamTraits<ArrayOfRemoteAudioData*>;
+ ~ArrayOfRemoteAudioData() = default;
+
+ nsTArray<RemoteAudioData> mSamples;
+ RemoteArrayOfByteBuffer mBuffers;
+};
+
+namespace ipc {
+
+template <>
+struct IPDLParamTraits<RemoteVideoData> {
+ typedef RemoteVideoData paramType;
+ static void Write(IPC::Message* aMsg, ipc::IProtocol* aActor,
+ paramType&& aVar) {
+ WriteIPDLParam(aMsg, aActor, std::move(aVar.mBase));
+ WriteIPDLParam(aMsg, aActor, std::move(aVar.mDisplay));
+ WriteIPDLParam(aMsg, aActor, std::move(aVar.mImage));
+ aMsg->WriteBytes(&aVar.mFrameID, 4);
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ mozilla::ipc::IProtocol* aActor, paramType* aVar) {
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &aVar->mBase) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &aVar->mDisplay) ||
+ !ReadIPDLParam(aMsg, aIter, aActor, &aVar->mImage) ||
+ !aMsg->ReadBytesInto(aIter, &aVar->mFrameID, 4)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<ArrayOfRemoteVideoData*> {
+ typedef ArrayOfRemoteVideoData paramType;
+ static void Write(IPC::Message* aMsg, mozilla::ipc::IProtocol* aActor,
+ paramType* aVar) {
+ WriteIPDLParam(aMsg, aActor, std::move(aVar->mArray));
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ ipc::IProtocol* aActor, RefPtr<paramType>* aVar) {
+ nsTArray<RemoteVideoData> array;
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &array)) {
+ return false;
+ }
+ auto results = MakeRefPtr<ArrayOfRemoteVideoData>(std::move(array));
+ *aVar = std::move(results);
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<RemoteArrayOfByteBuffer> {
+ typedef RemoteArrayOfByteBuffer paramType;
+ // We do not want to move the RemoteArrayOfByteBuffer as we want to recycle
+ // the shmem it contains for another time.
+ static void Write(IPC::Message* aMsg, ipc::IProtocol* aActor,
+ const paramType& aVar);
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ ipc::IProtocol* aActor, paramType* aVar);
+};
+
+template <>
+struct IPDLParamTraits<ArrayOfRemoteMediaRawData::RemoteMediaRawData> {
+ typedef ArrayOfRemoteMediaRawData::RemoteMediaRawData paramType;
+ static void Write(IPC::Message* aMsg, ipc::IProtocol* aActor,
+ const paramType& aVar);
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ ipc::IProtocol* aActor, paramType* aVar);
+};
+
+template <>
+struct IPDLParamTraits<ArrayOfRemoteMediaRawData*> {
+ typedef ArrayOfRemoteMediaRawData paramType;
+ static void Write(IPC::Message* aMsg, ipc::IProtocol* aActor,
+ paramType* aVar);
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ ipc::IProtocol* aActor, RefPtr<paramType>* aVar);
+};
+
+template <>
+struct IPDLParamTraits<ArrayOfRemoteAudioData::RemoteAudioData> {
+ typedef ArrayOfRemoteAudioData::RemoteAudioData paramType;
+ static void Write(IPC::Message* aMsg, ipc::IProtocol* aActor,
+ const paramType& aVar);
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ ipc::IProtocol* aActor, paramType* aVar);
+};
+
+template <>
+struct IPDLParamTraits<ArrayOfRemoteAudioData*> {
+ typedef ArrayOfRemoteAudioData paramType;
+ static void Write(IPC::Message* aMsg, ipc::IProtocol* aActor,
+ paramType* aVar);
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ ipc::IProtocol* aActor, RefPtr<paramType>* aVar);
+};
+} // namespace ipc
+
+} // namespace mozilla
+
+#endif // mozilla_dom_media_ipc_RemoteMediaData_h
diff --git a/dom/media/ipc/RemoteMediaDataDecoder.cpp b/dom/media/ipc/RemoteMediaDataDecoder.cpp
new file mode 100644
index 0000000000..740f38721c
--- /dev/null
+++ b/dom/media/ipc/RemoteMediaDataDecoder.cpp
@@ -0,0 +1,140 @@
+/* -*- 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 "RemoteMediaDataDecoder.h"
+
+#include "RemoteDecoderChild.h"
+#include "RemoteDecoderManagerChild.h"
+
+namespace mozilla {
+
+RemoteMediaDataDecoder::RemoteMediaDataDecoder(RemoteDecoderChild* aChild)
+ : mChild(aChild) {}
+
+RemoteMediaDataDecoder::~RemoteMediaDataDecoder() {
+ if (mChild) {
+ // Shutdown didn't get called. This can happen if the creation of the
+ // decoder got interrupted while pending.
+ nsCOMPtr<nsISerialEventTarget> thread =
+ RemoteDecoderManagerChild::GetManagerThread();
+ MOZ_ASSERT(thread);
+ thread->Dispatch(NS_NewRunnableFunction(
+ "RemoteMediaDataDecoderShutdown", [child = std::move(mChild), thread] {
+ child->Shutdown()->Then(
+ thread, __func__,
+ [child](const ShutdownPromise::ResolveOrRejectValue& aValue) {
+ child->DestroyIPDL();
+ });
+ }));
+ }
+}
+
+RefPtr<MediaDataDecoder::InitPromise> RemoteMediaDataDecoder::Init() {
+ RefPtr<RemoteMediaDataDecoder> self = this;
+ return InvokeAsync(RemoteDecoderManagerChild::GetManagerThread(), __func__,
+ [self]() { return self->mChild->Init(); })
+ ->Then(
+ RemoteDecoderManagerChild::GetManagerThread(), __func__,
+ [self, this](TrackType aTrack) {
+ // If shutdown has started in the meantime shutdown promise may
+ // be resloved before this task. In this case mChild will be null
+ // and the init promise has to be canceled.
+ if (!mChild) {
+ return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_CANCELED,
+ __func__);
+ }
+ mDescription = mChild->GetDescriptionName();
+ mIsHardwareAccelerated =
+ mChild->IsHardwareAccelerated(mHardwareAcceleratedReason);
+ mConversion = mChild->NeedsConversion();
+ return InitPromise::CreateAndResolve(aTrack, __func__);
+ },
+ [self](const MediaResult& aError) {
+ return InitPromise::CreateAndReject(aError, __func__);
+ });
+}
+
+RefPtr<MediaDataDecoder::DecodePromise> RemoteMediaDataDecoder::Decode(
+ MediaRawData* aSample) {
+ RefPtr<RemoteMediaDataDecoder> self = this;
+ RefPtr<MediaRawData> sample = aSample;
+ return InvokeAsync(
+ RemoteDecoderManagerChild::GetManagerThread(), __func__,
+ [self, sample]() {
+ return self->mChild->Decode(nsTArray<RefPtr<MediaRawData>>{sample});
+ });
+}
+
+RefPtr<MediaDataDecoder::DecodePromise> RemoteMediaDataDecoder::DecodeBatch(
+ nsTArray<RefPtr<MediaRawData>>&& aSamples) {
+ RefPtr<RemoteMediaDataDecoder> self = this;
+ return InvokeAsync(RemoteDecoderManagerChild::GetManagerThread(), __func__,
+ [self, samples = std::move(aSamples)]() {
+ return self->mChild->Decode(samples);
+ });
+}
+
+RefPtr<MediaDataDecoder::FlushPromise> RemoteMediaDataDecoder::Flush() {
+ RefPtr<RemoteMediaDataDecoder> self = this;
+ return InvokeAsync(RemoteDecoderManagerChild::GetManagerThread(), __func__,
+ [self]() { return self->mChild->Flush(); });
+}
+
+RefPtr<MediaDataDecoder::DecodePromise> RemoteMediaDataDecoder::Drain() {
+ RefPtr<RemoteMediaDataDecoder> self = this;
+ return InvokeAsync(RemoteDecoderManagerChild::GetManagerThread(), __func__,
+ [self]() { return self->mChild->Drain(); });
+}
+
+RefPtr<ShutdownPromise> RemoteMediaDataDecoder::Shutdown() {
+ RefPtr<RemoteMediaDataDecoder> self = this;
+ return InvokeAsync(
+ RemoteDecoderManagerChild::GetManagerThread(), __func__, [self]() {
+ RefPtr<ShutdownPromise> p = self->mChild->Shutdown();
+
+ // We're about to be destroyed and drop our ref to
+ // *DecoderChild. Make sure we put a ref into the
+ // task queue for the *DecoderChild thread to keep
+ // it alive until we send the delete message.
+ p->Then(RemoteDecoderManagerChild::GetManagerThread(), __func__,
+ [child = std::move(self->mChild)](
+ const ShutdownPromise::ResolveOrRejectValue& aValue) {
+ MOZ_ASSERT(aValue.IsResolve());
+ child->DestroyIPDL();
+ return ShutdownPromise::CreateAndResolveOrReject(aValue,
+ __func__);
+ });
+ return p;
+ });
+}
+
+bool RemoteMediaDataDecoder::IsHardwareAccelerated(
+ nsACString& aFailureReason) const {
+ aFailureReason = mHardwareAcceleratedReason;
+ return mIsHardwareAccelerated;
+}
+
+void RemoteMediaDataDecoder::SetSeekThreshold(const media::TimeUnit& aTime) {
+ RefPtr<RemoteMediaDataDecoder> self = this;
+ media::TimeUnit time = aTime;
+ RemoteDecoderManagerChild::GetManagerThread()->Dispatch(
+ NS_NewRunnableFunction("dom::RemoteMediaDataDecoder::SetSeekThreshold",
+ [=]() {
+ MOZ_ASSERT(self->mChild);
+ self->mChild->SetSeekThreshold(time);
+ }),
+ NS_DISPATCH_NORMAL);
+}
+
+MediaDataDecoder::ConversionRequired RemoteMediaDataDecoder::NeedsConversion()
+ const {
+ return mConversion;
+}
+
+nsCString RemoteMediaDataDecoder::GetDescriptionName() const {
+ return mDescription;
+}
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RemoteMediaDataDecoder.h b/dom/media/ipc/RemoteMediaDataDecoder.h
new file mode 100644
index 0000000000..6e5ff8c406
--- /dev/null
+++ b/dom/media/ipc/RemoteMediaDataDecoder.h
@@ -0,0 +1,62 @@
+/* -*- 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/. */
+#ifndef include_dom_media_ipc_RemoteMediaDataDecoder_h
+#define include_dom_media_ipc_RemoteMediaDataDecoder_h
+#include "PlatformDecoderModule.h"
+
+#include "MediaData.h"
+
+namespace mozilla {
+
+class RemoteDecoderChild;
+class RemoteDecoderManagerChild;
+class RemoteMediaDataDecoder;
+
+DDLoggedTypeCustomNameAndBase(RemoteMediaDataDecoder, RemoteMediaDataDecoder,
+ MediaDataDecoder);
+
+// A MediaDataDecoder implementation that proxies through IPDL
+// to a 'real' decoder in the GPU or RDD process.
+// All requests get forwarded to a *DecoderChild instance that
+// operates solely on the provided manager and abstract manager threads.
+class RemoteMediaDataDecoder
+ : public MediaDataDecoder,
+ public DecoderDoctorLifeLogger<RemoteMediaDataDecoder> {
+ public:
+ explicit RemoteMediaDataDecoder(RemoteDecoderChild* aChild);
+
+ // MediaDataDecoder
+ RefPtr<InitPromise> Init() override;
+ RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
+ bool CanDecodeBatch() const override { return true; }
+ RefPtr<DecodePromise> DecodeBatch(
+ nsTArray<RefPtr<MediaRawData>>&& aSamples) override;
+ RefPtr<DecodePromise> Drain() override;
+ RefPtr<FlushPromise> Flush() override;
+ RefPtr<ShutdownPromise> Shutdown() override;
+ bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
+ void SetSeekThreshold(const media::TimeUnit& aTime) override;
+ nsCString GetDescriptionName() const override;
+ ConversionRequired NeedsConversion() const override;
+
+ private:
+ ~RemoteMediaDataDecoder();
+
+ // Only ever written to from the reader task queue (during the constructor and
+ // destructor when we can guarantee no other threads are accessing it). Only
+ // read from the manager thread.
+ RefPtr<RemoteDecoderChild> mChild;
+ // Only ever written/modified during decoder initialisation.
+ // As such can be accessed from any threads after that.
+ nsCString mDescription = "RemoteMediaDataDecoder"_ns;
+ bool mIsHardwareAccelerated = false;
+ nsCString mHardwareAcceleratedReason;
+ ConversionRequired mConversion = ConversionRequired::kNeedNone;
+};
+
+} // namespace mozilla
+
+#endif // include_dom_media_ipc_RemoteMediaDataDecoder_h
diff --git a/dom/media/ipc/RemoteVideoDecoder.cpp b/dom/media/ipc/RemoteVideoDecoder.cpp
new file mode 100644
index 0000000000..ad65cd8d25
--- /dev/null
+++ b/dom/media/ipc/RemoteVideoDecoder.cpp
@@ -0,0 +1,287 @@
+/* -*- 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 "RemoteVideoDecoder.h"
+
+#include "mozilla/layers/ImageDataSerializer.h"
+
+#ifdef MOZ_AV1
+# include "AOMDecoder.h"
+# include "DAV1DDecoder.h"
+#endif
+#ifdef XP_WIN
+# include "WMFDecoderModule.h"
+#endif
+#include "GPUVideoImage.h"
+#include "ImageContainer.h" // for PlanarYCbCrData and BufferRecycleBin
+#include "MediaDataDecoderProxy.h"
+#include "MediaInfo.h"
+#include "PDMFactory.h"
+#include "RemoteDecoderManagerParent.h"
+#include "RemoteImageHolder.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/layers/ImageClient.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/VideoBridgeChild.h"
+
+namespace mozilla {
+
+using namespace layers; // for PlanarYCbCrData and BufferRecycleBin
+using namespace ipc;
+using namespace gfx;
+
+layers::TextureForwarder* KnowsCompositorVideo::GetTextureForwarder() {
+ auto* vbc = VideoBridgeChild::GetSingleton();
+ return (vbc && vbc->CanSend()) ? vbc : nullptr;
+}
+layers::LayersIPCActor* KnowsCompositorVideo::GetLayersIPCActor() {
+ return GetTextureForwarder();
+}
+
+/* static */ already_AddRefed<KnowsCompositorVideo>
+KnowsCompositorVideo::TryCreateForIdentifier(
+ const layers::TextureFactoryIdentifier& aIdentifier) {
+ VideoBridgeChild* child = VideoBridgeChild::GetSingleton();
+ if (!child) {
+ return nullptr;
+ }
+
+ RefPtr<KnowsCompositorVideo> knowsCompositor = new KnowsCompositorVideo();
+ knowsCompositor->IdentifyTextureHost(aIdentifier);
+ return knowsCompositor.forget();
+}
+
+RemoteVideoDecoderChild::RemoteVideoDecoderChild(RemoteDecodeIn aLocation)
+ : RemoteDecoderChild(aLocation), mBufferRecycleBin(new BufferRecycleBin) {}
+
+MediaResult RemoteVideoDecoderChild::ProcessOutput(
+ DecodedOutputIPDL&& aDecodedData) {
+ AssertOnManagerThread();
+ MOZ_ASSERT(aDecodedData.type() == DecodedOutputIPDL::TArrayOfRemoteVideoData);
+
+ nsTArray<RemoteVideoData>& arrayData =
+ aDecodedData.get_ArrayOfRemoteVideoData()->Array();
+
+ for (auto&& data : arrayData) {
+ if (data.image().IsEmpty()) {
+ // This is a NullData object.
+ mDecodedData.AppendElement(MakeRefPtr<NullData>(
+ data.base().offset(), data.base().time(), data.base().duration()));
+ continue;
+ }
+ RefPtr<Image> image = data.image().TransferToImage(mBufferRecycleBin);
+
+ RefPtr<VideoData> video = VideoData::CreateFromImage(
+ data.display(), data.base().offset(), data.base().time(),
+ data.base().duration(), image, data.base().keyframe(),
+ data.base().timecode());
+
+ if (!video) {
+ // OOM
+ return MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__);
+ }
+ mDecodedData.AppendElement(std::move(video));
+ }
+ return NS_OK;
+}
+
+MediaResult RemoteVideoDecoderChild::InitIPDL(
+ const VideoInfo& aVideoInfo, float aFramerate,
+ const CreateDecoderParams::OptionSet& aOptions,
+ Maybe<layers::TextureFactoryIdentifier> aIdentifier) {
+ MOZ_ASSERT_IF(mLocation == RemoteDecodeIn::GpuProcess, aIdentifier);
+
+ RefPtr<RemoteDecoderManagerChild> manager =
+ RemoteDecoderManagerChild::GetSingleton(mLocation);
+
+ // The manager isn't available because RemoteDecoderManagerChild has been
+ // initialized with null end points and we don't want to decode video on RDD
+ // process anymore. Return false here so that we can fallback to other PDMs.
+ if (!manager) {
+ return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ RESULT_DETAIL("RemoteDecoderManager is not available."));
+ }
+
+ if (!manager->CanSend()) {
+ if (mLocation == RemoteDecodeIn::GpuProcess) {
+ // The manager doesn't support sending messages because we've just crashed
+ // and are working on reinitialization. Don't initialize mIPDLSelfRef and
+ // leave us in an error state. We'll then immediately reject the promise
+ // when Init() is called and the caller can try again. Hopefully by then
+ // the new manager is ready, or we've notified the caller of it being no
+ // longer available. If not, then the cycle repeats until we're ready.
+ return NS_OK;
+ }
+
+ return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ RESULT_DETAIL("RemoteDecoderManager unable to send."));
+ }
+
+ mIPDLSelfRef = this;
+ VideoDecoderInfoIPDL decoderInfo(aVideoInfo, aFramerate);
+ Unused << manager->SendPRemoteDecoderConstructor(this, decoderInfo, aOptions,
+ aIdentifier);
+
+ return NS_OK;
+}
+
+RemoteVideoDecoderParent::RemoteVideoDecoderParent(
+ RemoteDecoderManagerParent* aParent, const VideoInfo& aVideoInfo,
+ float aFramerate, const CreateDecoderParams::OptionSet& aOptions,
+ const Maybe<layers::TextureFactoryIdentifier>& aIdentifier,
+ nsISerialEventTarget* aManagerThread, TaskQueue* aDecodeTaskQueue)
+ : RemoteDecoderParent(aParent, aOptions, aManagerThread, aDecodeTaskQueue),
+ mVideoInfo(aVideoInfo),
+ mFramerate(aFramerate) {
+ if (aIdentifier) {
+ // Check to see if we have a direct PVideoBridge connection to the
+ // destination process specified in aIdentifier, and create a
+ // KnowsCompositor representing that connection if so. If this fails, then
+ // we fall back to returning the decoded frames directly via Output().
+ mKnowsCompositor =
+ KnowsCompositorVideo::TryCreateForIdentifier(*aIdentifier);
+ }
+}
+
+IPCResult RemoteVideoDecoderParent::RecvConstruct(
+ ConstructResolver&& aResolver) {
+ auto imageContainer = MakeRefPtr<layers::ImageContainer>();
+ if (mKnowsCompositor && XRE_IsRDDProcess()) {
+ // Ensure to allocate recycle allocator
+ imageContainer->EnsureRecycleAllocatorForRDD(mKnowsCompositor);
+ }
+ auto params = CreateDecoderParams{
+ mVideoInfo, mKnowsCompositor,
+ imageContainer, CreateDecoderParams::VideoFrameRate(mFramerate),
+ mOptions, CreateDecoderParams::NoWrapper(true),
+ };
+
+ mParent->EnsurePDMFactory().CreateDecoder(params)->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [resolver = std::move(aResolver), self = RefPtr{this}](
+ PlatformDecoderModule::CreateDecoderPromise::ResolveOrRejectValue&&
+ aValue) {
+ if (aValue.IsReject()) {
+ resolver(aValue.RejectValue());
+ return;
+ }
+ MOZ_ASSERT(aValue.ResolveValue());
+ self->mDecoder =
+ new MediaDataDecoderProxy(aValue.ResolveValue().forget(),
+ do_AddRef(self->mDecodeTaskQueue.get()));
+ resolver(NS_OK);
+ });
+ return IPC_OK();
+}
+
+MediaResult RemoteVideoDecoderParent::ProcessDecodedData(
+ MediaDataDecoder::DecodedData&& aData, DecodedOutputIPDL& aDecodedData) {
+ MOZ_ASSERT(OnManagerThread());
+
+ // If the video decoder bridge has shut down, stop.
+ if (mKnowsCompositor && !mKnowsCompositor->GetTextureForwarder()) {
+ aDecodedData = MakeRefPtr<ArrayOfRemoteVideoData>();
+ return NS_OK;
+ }
+
+ nsTArray<RemoteVideoData> array;
+
+ for (const auto& data : aData) {
+ MOZ_ASSERT(data->mType == MediaData::Type::VIDEO_DATA ||
+ data->mType == MediaData::Type::NULL_DATA,
+ "Can only decode videos using RemoteDecoderParent!");
+ if (data->mType == MediaData::Type::NULL_DATA) {
+ RemoteVideoData output(
+ MediaDataIPDL(data->mOffset, data->mTime, data->mTimecode,
+ data->mDuration, data->mKeyframe),
+ IntSize(), RemoteImageHolder(), -1);
+
+ array.AppendElement(std::move(output));
+ continue;
+ }
+ VideoData* video = static_cast<VideoData*>(data.get());
+
+ MOZ_ASSERT(video->mImage,
+ "Decoded video must output a layer::Image to "
+ "be used with RemoteDecoderParent");
+
+ RefPtr<TextureClient> texture;
+ SurfaceDescriptor sd;
+ IntSize size;
+ bool needStorage = false;
+
+ if (mKnowsCompositor) {
+ texture = video->mImage->GetTextureClient(mKnowsCompositor);
+
+ if (!texture) {
+ texture = ImageClient::CreateTextureClientForImage(video->mImage,
+ mKnowsCompositor);
+ }
+
+ if (texture) {
+ if (!texture->IsAddedToCompositableClient()) {
+ texture->InitIPDLActor(mKnowsCompositor);
+ texture->SetAddedToCompositableClient();
+ }
+ needStorage = true;
+ SurfaceDescriptorRemoteDecoder remoteSD;
+ texture->GetSurfaceDescriptorRemoteDecoder(&remoteSD);
+ sd = remoteSD;
+ size = texture->GetSize();
+ }
+ }
+
+ // If failed to create a GPU accelerated surface descriptor, fall back to
+ // copying frames via shmem.
+ if (!IsSurfaceDescriptorValid(sd)) {
+ needStorage = false;
+ PlanarYCbCrImage* image = video->mImage->AsPlanarYCbCrImage();
+ if (!image) {
+ return MediaResult(NS_ERROR_UNEXPECTED,
+ "Expected Planar YCbCr image in "
+ "RemoteVideoDecoderParent::ProcessDecodedData");
+ }
+
+ SurfaceDescriptorBuffer sdBuffer;
+ ShmemBuffer buffer = AllocateBuffer(image->GetDataSize());
+ if (!buffer.Valid()) {
+ return MediaResult(NS_ERROR_OUT_OF_MEMORY,
+ "AllocShmem failed in "
+ "RemoteVideoDecoderParent::ProcessDecodedData");
+ }
+
+ sdBuffer.data() = std::move(buffer.Get());
+ image->BuildSurfaceDescriptorBuffer(sdBuffer);
+
+ sd = sdBuffer;
+ size = image->GetSize();
+ }
+
+ if (needStorage) {
+ MOZ_ASSERT(sd.type() != SurfaceDescriptor::TSurfaceDescriptorBuffer);
+ mParent->StoreImage(static_cast<const SurfaceDescriptorGPUVideo&>(sd),
+ video->mImage, texture);
+ }
+
+ RemoteVideoData output(
+ MediaDataIPDL(data->mOffset, data->mTime, data->mTimecode,
+ data->mDuration, data->mKeyframe),
+ video->mDisplay,
+ RemoteImageHolder(mParent,
+ XRE_IsGPUProcess() ? VideoBridgeSource::GpuProcess
+ : VideoBridgeSource::RddProcess,
+ size, sd),
+ video->mFrameID);
+
+ array.AppendElement(std::move(output));
+ }
+
+ aDecodedData = MakeRefPtr<ArrayOfRemoteVideoData>(std::move(array));
+
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/dom/media/ipc/RemoteVideoDecoder.h b/dom/media/ipc/RemoteVideoDecoder.h
new file mode 100644
index 0000000000..52bcdca9be
--- /dev/null
+++ b/dom/media/ipc/RemoteVideoDecoder.h
@@ -0,0 +1,79 @@
+/* -*- 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/. */
+#ifndef include_dom_media_ipc_RemoteVideoDecoderChild_h
+#define include_dom_media_ipc_RemoteVideoDecoderChild_h
+#include "RemoteDecoderChild.h"
+#include "RemoteDecoderManagerChild.h"
+#include "RemoteDecoderParent.h"
+
+namespace mozilla {
+namespace layers {
+class BufferRecycleBin;
+} // namespace layers
+} // namespace mozilla
+
+namespace mozilla {
+
+class KnowsCompositorVideo : public layers::KnowsCompositor {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(KnowsCompositorVideo, override)
+
+ layers::TextureForwarder* GetTextureForwarder() override;
+ layers::LayersIPCActor* GetLayersIPCActor() override;
+
+ static already_AddRefed<KnowsCompositorVideo> TryCreateForIdentifier(
+ const layers::TextureFactoryIdentifier& aIdentifier);
+
+ private:
+ KnowsCompositorVideo() = default;
+ virtual ~KnowsCompositorVideo() = default;
+};
+
+using mozilla::ipc::IPCResult;
+
+class RemoteVideoDecoderChild : public RemoteDecoderChild {
+ public:
+ explicit RemoteVideoDecoderChild(RemoteDecodeIn aLocation);
+
+ MOZ_IS_CLASS_INIT MediaResult
+ InitIPDL(const VideoInfo& aVideoInfo, float aFramerate,
+ const CreateDecoderParams::OptionSet& aOptions,
+ mozilla::Maybe<layers::TextureFactoryIdentifier> aIdentifier);
+
+ MediaResult ProcessOutput(DecodedOutputIPDL&& aDecodedData) override;
+
+ private:
+ RefPtr<mozilla::layers::BufferRecycleBin> mBufferRecycleBin;
+};
+
+class RemoteVideoDecoderParent final : public RemoteDecoderParent {
+ public:
+ RemoteVideoDecoderParent(
+ RemoteDecoderManagerParent* aParent, const VideoInfo& aVideoInfo,
+ float aFramerate, const CreateDecoderParams::OptionSet& aOptions,
+ const Maybe<layers::TextureFactoryIdentifier>& aIdentifier,
+ nsISerialEventTarget* aManagerThread, TaskQueue* aDecodeTaskQueue);
+
+ protected:
+ IPCResult RecvConstruct(ConstructResolver&& aResolver) override;
+
+ MediaResult ProcessDecodedData(MediaDataDecoder::DecodedData&& aData,
+ DecodedOutputIPDL& aDecodedData) override;
+
+ private:
+ // Can only be accessed from the manager thread
+ // Note: we can't keep a reference to the original VideoInfo here
+ // because unlike in typical MediaDataDecoder situations, we're being
+ // passed a deserialized VideoInfo from RecvPRemoteDecoderConstructor
+ // which is destroyed when RecvPRemoteDecoderConstructor returns.
+ const VideoInfo mVideoInfo;
+ const float mFramerate;
+ RefPtr<KnowsCompositorVideo> mKnowsCompositor;
+};
+
+} // namespace mozilla
+
+#endif // include_dom_media_ipc_RemoteVideoDecoderChild_h
diff --git a/dom/media/ipc/ShmemRecycleAllocator.h b/dom/media/ipc/ShmemRecycleAllocator.h
new file mode 100644
index 0000000000..7207e27014
--- /dev/null
+++ b/dom/media/ipc/ShmemRecycleAllocator.h
@@ -0,0 +1,60 @@
+/* -*- 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/. */
+#ifndef include_dom_media_ipc_ShmemRecycleAllocator_h
+#define include_dom_media_ipc_ShmemRecycleAllocator_h
+
+#include "mozilla/ShmemPool.h"
+
+namespace mozilla {
+
+template <class T>
+class ShmemRecycleAllocator {
+ public:
+ explicit ShmemRecycleAllocator(T* aActor)
+ : mActor(aActor), mPool(1, ShmemPool::PoolType::DynamicPool) {}
+ ShmemBuffer AllocateBuffer(size_t aSize,
+ ShmemPool::AllocationPolicy aPolicy =
+ ShmemPool::AllocationPolicy::Unsafe) {
+ ShmemBuffer buffer = mPool.Get(mActor, aSize, aPolicy);
+ if (!buffer.Valid()) {
+ return buffer;
+ }
+ MOZ_DIAGNOSTIC_ASSERT(aSize <= buffer.Get().Size<uint8_t>());
+ mUsedShmems.AppendElement(buffer.Get());
+ mNeedCleanup = true;
+ return buffer;
+ }
+
+ void ReleaseBuffer(ShmemBuffer&& aBuffer) { mPool.Put(std::move(aBuffer)); }
+
+ void ReleaseAllBuffers() {
+ for (auto&& mem : mUsedShmems) {
+ ReleaseBuffer(ShmemBuffer(mem.Get()));
+ }
+ mUsedShmems.Clear();
+ }
+
+ void CleanupShmemRecycleAllocator() {
+ ReleaseAllBuffers();
+ mPool.Cleanup(mActor);
+ mNeedCleanup = false;
+ }
+
+ ~ShmemRecycleAllocator() {
+ MOZ_DIAGNOSTIC_ASSERT(mUsedShmems.IsEmpty() && !mNeedCleanup,
+ "Shmems not all deallocated");
+ }
+
+ private:
+ T* const mActor;
+ ShmemPool mPool;
+ AutoTArray<ShmemBuffer, 4> mUsedShmems;
+ bool mNeedCleanup = false;
+};
+
+} // namespace mozilla
+
+#endif // include_dom_media_ipc_ShmemRecycleAllocator_h
diff --git a/dom/media/ipc/moz.build b/dom/media/ipc/moz.build
new file mode 100644
index 0000000000..031cebc41d
--- /dev/null
+++ b/dom/media/ipc/moz.build
@@ -0,0 +1,65 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+
+IPDL_SOURCES += [
+ "PMediaDecoderParams.ipdlh",
+ "PRDD.ipdl",
+ "PRemoteDecoder.ipdl",
+ "PRemoteDecoderManager.ipdl",
+]
+
+EXPORTS.mozilla += [
+ "RDDChild.h",
+ "RDDParent.h",
+ "RDDProcessHost.h",
+ "RDDProcessImpl.h",
+ "RDDProcessManager.h",
+ "RemoteDecoderChild.h",
+ "RemoteDecoderManagerChild.h",
+ "RemoteDecoderManagerParent.h",
+ "RemoteDecoderModule.h",
+ "RemoteDecoderParent.h",
+ "RemoteImageHolder.h",
+ "RemoteMediaData.h",
+ "RemoteMediaDataDecoder.h",
+ "ShmemRecycleAllocator.h",
+]
+
+EXPORTS.mozilla.dom += [
+ "MediaIPCUtils.h",
+]
+
+SOURCES += [
+ "RDDChild.cpp",
+ "RDDParent.cpp",
+ "RDDProcessHost.cpp",
+ "RDDProcessImpl.cpp",
+ "RDDProcessManager.cpp",
+ "RemoteAudioDecoder.cpp",
+ "RemoteDecoderChild.cpp",
+ "RemoteDecoderManagerChild.cpp",
+ "RemoteDecoderManagerParent.cpp",
+ "RemoteDecoderModule.cpp",
+ "RemoteDecoderParent.cpp",
+ "RemoteImageHolder.cpp",
+ "RemoteMediaData.cpp",
+ "RemoteMediaDataDecoder.cpp",
+ "RemoteVideoDecoder.cpp",
+]
+
+# so we can include nsMacUtilsImpl.h in RDDParent.cpp for sandboxing
+LOCAL_INCLUDES += [
+ "/xpcom/base",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+# Add libFuzzer configuration directives
+include("/tools/fuzzing/libfuzzer-config.mozbuild")
+
+
+FINAL_LIBRARY = "xul"