diff --git a/dom/media/ipc/MediaIPCUtils.h b/dom/media/ipc/MediaIPCUtils.h index 8a50310d1866..51bed99e8d54 100644 --- a/dom/media/ipc/MediaIPCUtils.h +++ b/dom/media/ipc/MediaIPCUtils.h @@ -44,6 +44,49 @@ namespace IPC { return false; } }; + + template<> + struct ParamTraits + { + 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); + } + + 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)) { + return true; + } + return false; + } + }; + + template<> + struct ParamTraits + : public ContiguousEnumSerializerInclusive< + mozilla::MediaDataDecoder::ConversionRequired, + mozilla::MediaDataDecoder::ConversionRequired(0), + mozilla::MediaDataDecoder::ConversionRequired( + mozilla::MediaDataDecoder::ConversionRequired::kNeedAnnexB)> {}; + } // namespace IPC #endif // mozilla_dom_media_MediaIPCUtils_h diff --git a/dom/media/ipc/PRemoteDecoderManager.ipdl b/dom/media/ipc/PRemoteDecoderManager.ipdl new file mode 100644 index 000000000000..ac8c502393a9 --- /dev/null +++ b/dom/media/ipc/PRemoteDecoderManager.ipdl @@ -0,0 +1,27 @@ +/* -*- 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 PRemoteVideoDecoder; +include "mozilla/dom/MediaIPCUtils.h"; + +using VideoInfo from "MediaInfo.h"; +using AudioInfo from "MediaInfo.h"; +using mozilla::CreateDecoderParams::OptionSet from "PlatformDecoderModule.h"; + +namespace mozilla { + +sync protocol PRemoteDecoderManager +{ + manages PRemoteVideoDecoder; + +parent: + sync PRemoteVideoDecoder(VideoInfo info, + float framerate, + OptionSet options) + returns (bool success, + nsCString aErrorDescription); +}; + +} // namespace mozilla diff --git a/dom/media/ipc/PRemoteVideoDecoder.ipdl b/dom/media/ipc/PRemoteVideoDecoder.ipdl new file mode 100644 index 000000000000..62b6e37b53cb --- /dev/null +++ b/dom/media/ipc/PRemoteVideoDecoder.ipdl @@ -0,0 +1,60 @@ +/* -*- 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; +include PMediaDecoderParams; +include LayersSurfaces; +using mozilla::MediaDataDecoder::ConversionRequired from "PlatformDecoderModule.h"; + +namespace mozilla { + +struct RemoteVideoDataIPDL +{ + MediaDataIPDL base; + IntSize display; + IntSize frameSize; + SurfaceDescriptorBuffer sdBuffer; + int32_t frameID; +}; + +// This protocol provides a way to use MediaDataDecoder across processes. +// The parent side currently is only implemented to work with +// RemoteDecoderModule. +// The child side runs in the content process, and the parent side runs +// in the RDD process. We run a separate IPDL thread for both sides. +async protocol PRemoteVideoDecoder +{ + manager PRemoteDecoderManager; +parent: + async Init(); + + async Input(MediaRawDataIPDL data); + + async Flush(); + async Drain(); + async Shutdown(); + + async SetSeekThreshold(int64_t time); + + async __delete__(); + +child: + async InitComplete(nsCString decoderDescription, + ConversionRequired conversion); + async InitFailed(nsresult reason); + + async FlushComplete(); + + // Each output includes a SurfaceDescriptorBuffer that represents the decoded + // frame. + async VideoOutput(RemoteVideoDataIPDL data); + async InputExhausted(); + async DrainComplete(); + async Error(nsresult error); +}; + +} // namespace mozilla diff --git a/dom/media/ipc/RemoteDecoderManagerChild.cpp b/dom/media/ipc/RemoteDecoderManagerChild.cpp new file mode 100644 index 000000000000..7396de043efc --- /dev/null +++ b/dom/media/ipc/RemoteDecoderManagerChild.cpp @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 "base/task.h" + +#include "RemoteVideoDecoderChild.h" + +namespace mozilla { + +// Only modified on the main-thread +StaticRefPtr sRemoteDecoderManagerChildThread; +StaticRefPtr sRemoteDecoderManagerChildAbstractThread; + +// Only accessed from sRemoteDecoderManagerChildThread +static StaticRefPtr sRemoteDecoderManagerChild; + +/* static */ void +RemoteDecoderManagerChild::InitializeThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!sRemoteDecoderManagerChildThread) { + RefPtr childThread; + nsresult rv = NS_NewNamedThread("RemVidChild", getter_AddRefs(childThread)); + NS_ENSURE_SUCCESS_VOID(rv); + sRemoteDecoderManagerChildThread = childThread; + + sRemoteDecoderManagerChildAbstractThread = + AbstractThread::CreateXPCOMThreadWrapper(childThread, false); + } +} + +/* static */ void +RemoteDecoderManagerChild::InitForContent( + Endpoint&& aVideoManager) +{ + InitializeThread(); + sRemoteDecoderManagerChildThread->Dispatch( + NewRunnableFunction("InitForContentRunnable", + &Open, + std::move(aVideoManager)), + NS_DISPATCH_NORMAL); +} + +/* static */ void +RemoteDecoderManagerChild::Shutdown() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (sRemoteDecoderManagerChildThread) { + sRemoteDecoderManagerChildThread->Dispatch( + NS_NewRunnableFunction("dom::RemoteDecoderManagerChild::Shutdown", + []() { + if (sRemoteDecoderManagerChild && + sRemoteDecoderManagerChild->CanSend()) { + sRemoteDecoderManagerChild->Close(); + sRemoteDecoderManagerChild = nullptr; + } + }), + NS_DISPATCH_NORMAL); + + sRemoteDecoderManagerChildAbstractThread = nullptr; + sRemoteDecoderManagerChildThread->Shutdown(); + sRemoteDecoderManagerChildThread = nullptr; + } +} + +/* static */ RemoteDecoderManagerChild* +RemoteDecoderManagerChild::GetSingleton() +{ + MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread()); + return sRemoteDecoderManagerChild; +} + +/* static */ nsIThread* +RemoteDecoderManagerChild::GetManagerThread() +{ + return sRemoteDecoderManagerChildThread; +} + +/* static */ AbstractThread* +RemoteDecoderManagerChild::GetManagerAbstractThread() +{ + return sRemoteDecoderManagerChildAbstractThread; +} + +PRemoteVideoDecoderChild* +RemoteDecoderManagerChild::AllocPRemoteVideoDecoderChild( + const VideoInfo& /* not used */, + const float& /* not used */, + const CreateDecoderParams::OptionSet& /* not used */, + bool* /* not used */, + nsCString* /* not used */) +{ + return new RemoteVideoDecoderChild(); +} + +bool +RemoteDecoderManagerChild::DeallocPRemoteVideoDecoderChild( + PRemoteVideoDecoderChild* actor) +{ + RemoteVideoDecoderChild* child = static_cast(actor); + child->IPDLActorDestroyed(); + return true; +} + +void +RemoteDecoderManagerChild::Open( + Endpoint&& aEndpoint) +{ + sRemoteDecoderManagerChild = nullptr; + if (aEndpoint.IsValid()) { + RefPtr manager = new RemoteDecoderManagerChild(); + if (aEndpoint.Bind(manager)) { + sRemoteDecoderManagerChild = manager; + manager->InitIPDL(); + } + } +} + +void +RemoteDecoderManagerChild::InitIPDL() +{ + mCanSend = true; + mIPDLSelfRef = this; +} + +void +RemoteDecoderManagerChild::ActorDestroy(ActorDestroyReason aWhy) +{ + mCanSend = false; +} + +void +RemoteDecoderManagerChild::DeallocPRemoteDecoderManagerChild() +{ + mIPDLSelfRef = nullptr; +} + +bool +RemoteDecoderManagerChild::CanSend() +{ + MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread()); + return mCanSend; +} + +} // namespace mozilla diff --git a/dom/media/ipc/RemoteDecoderManagerChild.h b/dom/media/ipc/RemoteDecoderManagerChild.h new file mode 100644 index 000000000000..1e28a794d8cf --- /dev/null +++ b/dom/media/ipc/RemoteDecoderManagerChild.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 "mozilla/PRemoteDecoderManagerChild.h" + +namespace mozilla { + +class RemoteDecoderManagerChild final : public PRemoteDecoderManagerChild +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteDecoderManagerChild) + + // Can only be called from the manager thread + static RemoteDecoderManagerChild* GetSingleton(); + + // Can be called from any thread. + static nsIThread* GetManagerThread(); + static AbstractThread* GetManagerAbstractThread(); + + // Main thread only + static void InitForContent( + Endpoint&& aVideoManager); + static void Shutdown(); + + bool CanSend(); + +protected: + void InitIPDL(); + + void ActorDestroy(ActorDestroyReason aWhy) override; + void DeallocPRemoteDecoderManagerChild() override; + + PRemoteVideoDecoderChild* AllocPRemoteVideoDecoderChild( + const VideoInfo& aVideoInfo, + const float& aFramerate, + const CreateDecoderParams::OptionSet& aOptions, + bool* aSuccess, + nsCString* aErrorDescription) override; + bool DeallocPRemoteVideoDecoderChild( + PRemoteVideoDecoderChild* actor) override; + +private: + // Main thread only + static void InitializeThread(); + + RemoteDecoderManagerChild() = default; + ~RemoteDecoderManagerChild() = default; + + static void Open(Endpoint&& aEndpoint); + + RefPtr mIPDLSelfRef; + + // Should only ever be accessed on the manager thread. + bool mCanSend = false; +}; + +} // namespace mozilla + +#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 000000000000..fdddbb551469 --- /dev/null +++ b/dom/media/ipc/RemoteDecoderManagerParent.cpp @@ -0,0 +1,216 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "RemoteDecoderManagerParent.h" + +#if XP_WIN +#include +#endif + +#include "RemoteVideoDecoderParent.h" +#include "VideoUtils.h" // for MediaThreadType + +namespace mozilla { + +StaticRefPtr sRemoteDecoderManagerParentThread; +StaticRefPtr sRemoteDecoderManagerTaskQueue; + +class RemoteDecoderManagerThreadHolder +{ + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteDecoderManagerThreadHolder) + +public: + RemoteDecoderManagerThreadHolder() { } + +private: + ~RemoteDecoderManagerThreadHolder() + { + NS_DispatchToMainThread(NS_NewRunnableFunction( + "dom::RemoteDecoderManagerThreadHolder::~RemoteDecoderManagerThreadHolder", + []() { + sRemoteDecoderManagerParentThread->Shutdown(); + sRemoteDecoderManagerParentThread = nullptr; + })); + } +}; + +StaticRefPtr + sRemoteDecoderManagerParentThreadHolder; + +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::ShutdownThreads(); + return NS_OK; + } +}; +NS_IMPL_ISUPPORTS(RemoteDecoderManagerThreadShutdownObserver, nsIObserver); + +bool +RemoteDecoderManagerParent::StartupThreads() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (sRemoteDecoderManagerParentThread) { + return true; + } + + nsCOMPtr observerService = services::GetObserverService(); + if (!observerService) { + return false; + } + + RefPtr managerThread; + nsresult rv = + NS_NewNamedThread("RemVidParent", getter_AddRefs(managerThread)); + if (NS_FAILED(rv)) { + return false; + } + sRemoteDecoderManagerParentThread = managerThread; + sRemoteDecoderManagerParentThreadHolder = + new RemoteDecoderManagerThreadHolder(); +#if XP_WIN + sRemoteDecoderManagerParentThread->Dispatch( + NS_NewRunnableFunction( + "RemoteDecoderManagerParent::StartupThreads", + []() { + DebugOnly hr = CoInitializeEx(0, COINIT_MULTITHREADED); + MOZ_ASSERT(SUCCEEDED(hr)); + }), + NS_DISPATCH_NORMAL); +#endif + + sRemoteDecoderManagerTaskQueue = new TaskQueue( + managerThread.forget(), + "RemoteDecoderManagerParent::sRemoteDecoderManagerTaskQueue"); + + auto* obs = new RemoteDecoderManagerThreadShutdownObserver(); + observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); + return true; +} + +void +RemoteDecoderManagerParent::ShutdownThreads() +{ + sRemoteDecoderManagerTaskQueue = nullptr; + + sRemoteDecoderManagerParentThreadHolder = nullptr; + while (sRemoteDecoderManagerParentThread) { + NS_ProcessNextEvent(nullptr, true); + } +} + +bool +RemoteDecoderManagerParent::OnManagerThread() +{ + return NS_GetCurrentThread() == sRemoteDecoderManagerParentThread; +} + +bool +RemoteDecoderManagerParent::CreateForContent( + Endpoint&& aEndpoint) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_RDD); + MOZ_ASSERT(NS_IsMainThread()); + + if (!StartupThreads()) { + return false; + } + + RefPtr parent = + new RemoteDecoderManagerParent(sRemoteDecoderManagerParentThreadHolder); + + RefPtr task = + NewRunnableMethod&&>( + "dom::RemoteDecoderManagerParent::Open", + parent, + &RemoteDecoderManagerParent::Open, + std::move(aEndpoint)); + sRemoteDecoderManagerParentThread->Dispatch(task.forget(), + NS_DISPATCH_NORMAL); + return true; +} + +RemoteDecoderManagerParent::RemoteDecoderManagerParent( + RemoteDecoderManagerThreadHolder* aHolder) + : mThreadHolder(aHolder) +{ + MOZ_COUNT_CTOR(RemoteDecoderManagerParent); +} + +RemoteDecoderManagerParent::~RemoteDecoderManagerParent() +{ + MOZ_COUNT_DTOR(RemoteDecoderManagerParent); +} + +void +RemoteDecoderManagerParent::ActorDestroy( + mozilla::ipc::IProtocol::ActorDestroyReason) +{ + mThreadHolder = nullptr; +} + +PRemoteVideoDecoderParent* +RemoteDecoderManagerParent::AllocPRemoteVideoDecoderParent( + const VideoInfo& aVideoInfo, + const float& aFramerate, + const CreateDecoderParams::OptionSet& aOptions, + bool* aSuccess, + nsCString* aErrorDescription) +{ + RefPtr decodeTaskQueue = new TaskQueue( + GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER), + "RemoteVideoDecoderParent::mDecodeTaskQueue"); + + auto* parent = new RemoteVideoDecoderParent(this, + aVideoInfo, + aFramerate, + aOptions, + sRemoteDecoderManagerTaskQueue, + decodeTaskQueue, + aSuccess, + aErrorDescription); + + return parent; +} + +bool +RemoteDecoderManagerParent::DeallocPRemoteVideoDecoderParent( + PRemoteVideoDecoderParent* actor) +{ + RemoteVideoDecoderParent* parent = + static_cast(actor); + parent->Destroy(); + return true; +} + +void +RemoteDecoderManagerParent::Open( + Endpoint&& aEndpoint) +{ + if (!aEndpoint.Bind(this)) { + // We can't recover from this. + MOZ_CRASH("Failed to bind RemoteDecoderManagerParent to endpoint"); + } + AddRef(); +} + +void +RemoteDecoderManagerParent::DeallocPRemoteDecoderManagerParent() +{ + Release(); +} + +} // namespace mozilla diff --git a/dom/media/ipc/RemoteDecoderManagerParent.h b/dom/media/ipc/RemoteDecoderManagerParent.h new file mode 100644 index 000000000000..bed51b158a59 --- /dev/null +++ b/dom/media/ipc/RemoteDecoderManagerParent.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 "mozilla/PRemoteDecoderManagerParent.h" + +namespace mozilla { + +class RemoteDecoderManagerThreadHolder; + +class RemoteDecoderManagerParent final : public PRemoteDecoderManagerParent +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteDecoderManagerParent) + + static bool CreateForContent( + Endpoint&& aEndpoint); + + static bool StartupThreads(); + static void ShutdownThreads(); + + bool OnManagerThread(); + +protected: + PRemoteVideoDecoderParent* AllocPRemoteVideoDecoderParent( + const VideoInfo& aVideoInfo, + const float& aFramerate, + const CreateDecoderParams::OptionSet& aOptions, + bool* aSuccess, + nsCString* aErrorDescription) override; + bool DeallocPRemoteVideoDecoderParent( + PRemoteVideoDecoderParent* actor) override; + + void ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override; + + void DeallocPRemoteDecoderManagerParent() override; + +private: + explicit RemoteDecoderManagerParent( + RemoteDecoderManagerThreadHolder* aThreadHolder); + ~RemoteDecoderManagerParent(); + + void Open(Endpoint&& aEndpoint); + + RefPtr mThreadHolder; +}; + +} // 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 000000000000..53d864d616a6 --- /dev/null +++ b/dom/media/ipc/RemoteDecoderModule.cpp @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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" + +#include "base/thread.h" +#include "mozilla/layers/SynchronousTask.h" +#include "mozilla/StaticPrefs.h" + +#ifdef MOZ_AV1 +#include "AOMDecoder.h" +#endif +#include "RemoteDecoderManagerChild.h" +#include "RemoteMediaDataDecoder.h" +#include "RemoteVideoDecoderChild.h" + +namespace mozilla { + +using base::Thread; +using namespace ipc; +using namespace layers; +using namespace gfx; + +nsresult +RemoteDecoderModule::Startup() +{ + if (!RemoteDecoderManagerChild::GetManagerThread()) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +bool +RemoteDecoderModule::SupportsMimeType( + const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const +{ + bool supports = false; + +#ifdef MOZ_AV1 + if (StaticPrefs::MediaAv1Enabled()) { + supports |= AOMDecoder::IsAV1(aMimeType); + } +#endif + MOZ_LOG(sPDMLog, LogLevel::Debug, ("Sandbox decoder %s requested type", + supports ? "supports" : "rejects")); + return supports; +} + +already_AddRefed +RemoteDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams) +{ + RemoteVideoDecoderChild* child = new RemoteVideoDecoderChild(); + RefPtr object = + new RemoteMediaDataDecoder( + child, + RemoteDecoderManagerChild::GetManagerThread(), + RemoteDecoderManagerChild::GetManagerAbstractThread()); + + // (per Matt Woodrow) We can't use NS_DISPATCH_SYNC here since that + // can spin the event loop while it waits. + SynchronousTask task("InitIPDL"); + MediaResult result(NS_OK); + RemoteDecoderManagerChild::GetManagerThread()->Dispatch( + NS_NewRunnableFunction( + "dom::RemoteDecoderModule::CreateVideoDecoder", + [&]() { + AutoCompleteTask complete(&task); + result = child->InitIPDL( + aParams.VideoConfig(), + aParams.mRate.mValue, + aParams.mOptions); + }), + NS_DISPATCH_NORMAL); + task.Wait(); + + if (NS_FAILED(result)) { + if (aParams.mError) { + *aParams.mError = result; + } + return nullptr; + } + + return object.forget(); +} + +} // namespace mozilla diff --git a/dom/media/ipc/RemoteDecoderModule.h b/dom/media/ipc/RemoteDecoderModule.h new file mode 100644 index 000000000000..a7664c04e3ad --- /dev/null +++ b/dom/media/ipc/RemoteDecoderModule.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 { + +// A PDM implementation that creates a RemoteMediaDataDecoder (a +// MediaDataDecoder) that proxies to a RemoteVideoDecoderChild. +// A decoder child will talk to its respective decoder parent +// (RemoteVideoDecoderParent) on the RDD process. +class RemoteDecoderModule : public PlatformDecoderModule +{ +public: + RemoteDecoderModule() = default; + + nsresult Startup() override; + + bool SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const override; + + already_AddRefed CreateVideoDecoder( + const CreateDecoderParams& aParams) override; + + already_AddRefed CreateAudioDecoder( + const CreateDecoderParams& aParams) override + { + return nullptr; + } +}; + +} // namespace mozilla + +#endif // include_dom_media_ipc_RemoteDecoderModule_h diff --git a/dom/media/ipc/RemoteMediaDataDecoder.h b/dom/media/ipc/RemoteMediaDataDecoder.h index 375ee6f2febb..50e8af65c834 100644 --- a/dom/media/ipc/RemoteMediaDataDecoder.h +++ b/dom/media/ipc/RemoteMediaDataDecoder.h @@ -13,6 +13,7 @@ namespace mozilla { class GpuDecoderModule; class IRemoteDecoderChild; +class RemoteDecoderModule; class RemoteMediaDataDecoder; DDLoggedTypeCustomNameAndBase(RemoteMediaDataDecoder, @@ -29,6 +30,7 @@ class RemoteMediaDataDecoder { public: friend class GpuDecoderModule; + friend class RemoteDecoderModule; // MediaDataDecoder RefPtr Init() override; diff --git a/dom/media/ipc/RemoteVideoDecoderChild.cpp b/dom/media/ipc/RemoteVideoDecoderChild.cpp new file mode 100644 index 000000000000..de9cdb24981a --- /dev/null +++ b/dom/media/ipc/RemoteVideoDecoderChild.cpp @@ -0,0 +1,365 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 "RemoteVideoDecoderChild.h" + +#include "base/thread.h" +#include "mozilla/layers/ImageDataSerializer.h" + +#include "ImageContainer.h" // for PlanarYCbCrData and BufferRecycleBin +#include "RemoteDecoderManagerChild.h" + +namespace mozilla { + +using base::Thread; +using namespace layers; // for PlanarYCbCrData and BufferRecycleBin + +RemoteVideoDecoderChild::RemoteVideoDecoderChild() + : mThread(RemoteDecoderManagerChild::GetManagerThread()) + , mCanSend(false) + , mInitialized(false) + , mIsHardwareAccelerated(false) + , mConversion(MediaDataDecoder::ConversionRequired::kNeedNone) + , mBufferRecycleBin(new BufferRecycleBin) +{ +} + +RemoteVideoDecoderChild::~RemoteVideoDecoderChild() +{ + AssertOnManagerThread(); + mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); +} + +RefPtr +RemoteVideoDecoderChild::DeserializeImage( + const SurfaceDescriptorBuffer& aSdBuffer, + const IntSize& aPicSize) +{ + MOZ_ASSERT(aSdBuffer.desc().type() == BufferDescriptor::TYCbCrDescriptor); + if (aSdBuffer.desc().type() != BufferDescriptor::TYCbCrDescriptor) { + return nullptr; + } + const YCbCrDescriptor& descriptor = aSdBuffer.desc().get_YCbCrDescriptor(); + + uint8_t* buffer = nullptr; + const MemoryOrShmem& memOrShmem = aSdBuffer.data(); + switch (memOrShmem.type()) { + case MemoryOrShmem::Tuintptr_t: + buffer = reinterpret_cast(memOrShmem.get_uintptr_t()); + break; + case MemoryOrShmem::TShmem: + buffer = memOrShmem.get_Shmem().get(); + 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; + // default mPicX, mPicY because not held in YCbCrDescriptor + pData.mPicX = pData.mPicY = 0; + pData.mPicSize = aPicSize; + 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 image = + new RecyclingPlanarYCbCrImage(mBufferRecycleBin); + image->CopyData(pData); + + switch (memOrShmem.type()) { + case MemoryOrShmem::Tuintptr_t: + delete [] reinterpret_cast(memOrShmem.get_uintptr_t()); + break; + case MemoryOrShmem::TShmem: + DeallocShmem(memOrShmem.get_Shmem()); + break; + default: + MOZ_ASSERT(false, "Unknown MemoryOrShmem type"); + } + + return image; +} + +mozilla::ipc::IPCResult +RemoteVideoDecoderChild::RecvVideoOutput(const RemoteVideoDataIPDL& aData) +{ + AssertOnManagerThread(); + + RefPtr image = DeserializeImage(aData.sdBuffer(), aData.frameSize()); + + RefPtr video = VideoData::CreateFromImage( + aData.display(), + aData.base().offset(), + media::TimeUnit::FromMicroseconds(aData.base().time()), + media::TimeUnit::FromMicroseconds(aData.base().duration()), + image, + aData.base().keyframe(), + media::TimeUnit::FromMicroseconds(aData.base().timecode())); + + mDecodedData.AppendElement(std::move(video)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +RemoteVideoDecoderChild::RecvInputExhausted() +{ + AssertOnManagerThread(); + mDecodePromise.ResolveIfExists(mDecodedData, __func__); + mDecodedData.Clear(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +RemoteVideoDecoderChild::RecvDrainComplete() +{ + AssertOnManagerThread(); + mDrainPromise.ResolveIfExists(mDecodedData, __func__); + mDecodedData.Clear(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +RemoteVideoDecoderChild::RecvError(const nsresult& aError) +{ + AssertOnManagerThread(); + mDecodedData.Clear(); + mDecodePromise.RejectIfExists(aError, __func__); + mDrainPromise.RejectIfExists(aError, __func__); + mFlushPromise.RejectIfExists(aError, __func__); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +RemoteVideoDecoderChild::RecvInitComplete(const nsCString& aDecoderDescription, + const ConversionRequired& aConversion) +{ + AssertOnManagerThread(); + mInitPromise.ResolveIfExists(TrackInfo::kVideoTrack, __func__); + mInitialized = true; + mDescription = aDecoderDescription; + mConversion = aConversion; + return IPC_OK(); +} + +mozilla::ipc::IPCResult +RemoteVideoDecoderChild::RecvInitFailed(const nsresult& aReason) +{ + AssertOnManagerThread(); + mInitPromise.RejectIfExists(aReason, __func__); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +RemoteVideoDecoderChild::RecvFlushComplete() +{ + AssertOnManagerThread(); + mFlushPromise.ResolveIfExists(true, __func__); + return IPC_OK(); +} + +void +RemoteVideoDecoderChild::ActorDestroy(ActorDestroyReason aWhy) +{ + mCanSend = false; +} + +MediaResult +RemoteVideoDecoderChild::InitIPDL( + const VideoInfo& aVideoInfo, + float aFramerate, + const CreateDecoderParams::OptionSet& aOptions) +{ + RefPtr manager = + RemoteDecoderManagerChild::GetSingleton(); + + // 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; + bool success = false; + nsCString errorDescription; + if (manager->SendPRemoteVideoDecoderConstructor(this, + aVideoInfo, + aFramerate, + aOptions, + &success, + &errorDescription)) { + mCanSend = true; + } + + return success ? MediaResult(NS_OK) : + MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, errorDescription); +} + +void +RemoteVideoDecoderChild::DestroyIPDL() +{ + if (mCanSend) { + PRemoteVideoDecoderChild::Send__delete__(this); + } +} + +void +RemoteVideoDecoderChild::IPDLActorDestroyed() +{ + mIPDLSelfRef = nullptr; +} + +// MediaDataDecoder methods + +RefPtr +RemoteVideoDecoderChild::Init() +{ + AssertOnManagerThread(); + + if (!mIPDLSelfRef || !mCanSend) { + return MediaDataDecoder::InitPromise::CreateAndReject( + NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__); + } + + SendInit(); + + return mInitPromise.Ensure(__func__); +} + +RefPtr +RemoteVideoDecoderChild::Decode(MediaRawData* aSample) +{ + AssertOnManagerThread(); + + if (!mCanSend) { + return MediaDataDecoder::DecodePromise::CreateAndReject( + NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__); + } + + // TODO: It would be nice to add an allocator method to + // MediaDataDecoder so that the demuxer could write directly + // into shmem rather than requiring a copy here. + Shmem buffer; + if (!AllocShmem(aSample->Size(), Shmem::SharedMemory::TYPE_BASIC, &buffer)) { + return MediaDataDecoder::DecodePromise::CreateAndReject( + NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__); + } + + memcpy(buffer.get(), aSample->Data(), aSample->Size()); + + MediaRawDataIPDL sample(MediaDataIPDL(aSample->mOffset, + aSample->mTime.ToMicroseconds(), + aSample->mTimecode.ToMicroseconds(), + aSample->mDuration.ToMicroseconds(), + aSample->mFrames, + aSample->mKeyframe), + buffer); + SendInput(sample); + return mDecodePromise.Ensure(__func__); +} + +RefPtr +RemoteVideoDecoderChild::Flush() +{ + AssertOnManagerThread(); + mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); + mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); + if (!mCanSend) { + return MediaDataDecoder::FlushPromise::CreateAndReject( + NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__); + } + SendFlush(); + return mFlushPromise.Ensure(__func__); +} + +RefPtr +RemoteVideoDecoderChild::Drain() +{ + AssertOnManagerThread(); + if (!mCanSend) { + return MediaDataDecoder::DecodePromise::CreateAndReject( + NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__); + } + SendDrain(); + return mDrainPromise.Ensure(__func__); +} + +void +RemoteVideoDecoderChild::Shutdown() +{ + AssertOnManagerThread(); + mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); + if (mCanSend) { + SendShutdown(); + } + mInitialized = false; +} + +bool +RemoteVideoDecoderChild::IsHardwareAccelerated(nsACString& aFailureReason) const +{ + AssertOnManagerThread(); + aFailureReason = mHardwareAcceleratedReason; + return mIsHardwareAccelerated; +} + +nsCString +RemoteVideoDecoderChild::GetDescriptionName() const +{ + AssertOnManagerThread(); + return mDescription; +} + +void +RemoteVideoDecoderChild::SetSeekThreshold(const media::TimeUnit& aTime) +{ + AssertOnManagerThread(); + if (mCanSend) { + SendSetSeekThreshold(aTime.ToMicroseconds()); + } +} + +MediaDataDecoder::ConversionRequired +RemoteVideoDecoderChild::NeedsConversion() const +{ + AssertOnManagerThread(); + return mConversion; +} + +void +RemoteVideoDecoderChild::AssertOnManagerThread() const +{ + MOZ_ASSERT(NS_GetCurrentThread() == mThread); +} + +RemoteDecoderManagerChild* +RemoteVideoDecoderChild::GetManager() +{ + if (!mCanSend) { + return nullptr; + } + return static_cast(Manager()); +} + +} // namespace mozilla diff --git a/dom/media/ipc/RemoteVideoDecoderChild.h b/dom/media/ipc/RemoteVideoDecoderChild.h new file mode 100644 index 000000000000..b38fd16e6719 --- /dev/null +++ b/dom/media/ipc/RemoteVideoDecoderChild.h @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 "mozilla/PRemoteVideoDecoderChild.h" +#include "IRemoteDecoderChild.h" + +#include "MediaResult.h" + +namespace mozilla { +namespace layers { +class BufferRecycleBin; +} +} + +namespace mozilla { + +class RemoteDecoderManagerChild; +using mozilla::MediaDataDecoder; + +class RemoteVideoDecoderChild final : public PRemoteVideoDecoderChild + , public IRemoteDecoderChild +{ +public: + explicit RemoteVideoDecoderChild(); + + // PRemoteVideoDecoderChild + mozilla::ipc::IPCResult RecvVideoOutput( + const RemoteVideoDataIPDL& aData) override; + mozilla::ipc::IPCResult RecvInputExhausted() override; + mozilla::ipc::IPCResult RecvDrainComplete() override; + mozilla::ipc::IPCResult RecvError(const nsresult& aError) override; + mozilla::ipc::IPCResult RecvInitComplete( + const nsCString& aDecoderDescription, + const ConversionRequired& aConversion) override; + mozilla::ipc::IPCResult RecvInitFailed(const nsresult& aReason) override; + mozilla::ipc::IPCResult RecvFlushComplete() override; + + void ActorDestroy(ActorDestroyReason aWhy) override; + + // IRemoteDecoderChild + RefPtr Init() override; + RefPtr Decode(MediaRawData* aSample) override; + RefPtr Drain() override; + RefPtr Flush() override; + void Shutdown() override; + bool IsHardwareAccelerated(nsACString& aFailureReason) const override; + nsCString GetDescriptionName() const override; + void SetSeekThreshold(const media::TimeUnit& aTime) override; + MediaDataDecoder::ConversionRequired NeedsConversion() const override; + void DestroyIPDL() override; + + MOZ_IS_CLASS_INIT + MediaResult InitIPDL(const VideoInfo& aVideoInfo, + float aFramerate, + const CreateDecoderParams::OptionSet& aOptions); + + // Called from IPDL when our actor has been destroyed + void IPDLActorDestroyed(); + + RemoteDecoderManagerChild* GetManager(); + +private: + ~RemoteVideoDecoderChild(); + + void AssertOnManagerThread() const; + RefPtr DeserializeImage( + const SurfaceDescriptorBuffer& sdBuffer, + const IntSize& aPicSize); + + RefPtr mIPDLSelfRef; + RefPtr mThread; + + MozPromiseHolder mInitPromise; + MozPromiseHolder mDecodePromise; + MozPromiseHolder mDrainPromise; + MozPromiseHolder mFlushPromise; + + nsCString mHardwareAcceleratedReason; + nsCString mDescription; + bool mCanSend; + bool mInitialized; + bool mIsHardwareAccelerated; + MediaDataDecoder::ConversionRequired mConversion; + MediaDataDecoder::DecodedData mDecodedData; + RefPtr mBufferRecycleBin; +}; + +} // namespace mozilla + +#endif // include_dom_media_ipc_RemoteVideoDecoderChild_h diff --git a/dom/media/ipc/RemoteVideoDecoderParent.cpp b/dom/media/ipc/RemoteVideoDecoderParent.cpp new file mode 100644 index 000000000000..04f781ca54c2 --- /dev/null +++ b/dom/media/ipc/RemoteVideoDecoderParent.cpp @@ -0,0 +1,261 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 "RemoteVideoDecoderParent.h" + +#include "mozilla/Unused.h" + +#ifdef MOZ_AV1 +#include "AOMDecoder.h" +#endif +#include "ImageContainer.h" +#include "RemoteDecoderManagerParent.h" +#include "RemoteDecoderModule.h" + +namespace mozilla { + +using media::TimeUnit; +using namespace layers; // for PlanarYCbCrImage and BufferRecycleBin + +RemoteVideoDecoderParent::RemoteVideoDecoderParent( + RemoteDecoderManagerParent* aParent, + const VideoInfo& aVideoInfo, + float aFramerate, + const CreateDecoderParams::OptionSet& aOptions, + TaskQueue* aManagerTaskQueue, + TaskQueue* aDecodeTaskQueue, + bool* aSuccess, + nsCString* aErrorDescription) + : mParent(aParent) + , mManagerTaskQueue(aManagerTaskQueue) + , mDecodeTaskQueue(aDecodeTaskQueue) + , mDestroyed(false) + , mVideoInfo(aVideoInfo) +{ + MOZ_COUNT_CTOR(RemoteVideoDecoderParent); + 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; + + CreateDecoderParams params(mVideoInfo); + params.mTaskQueue = mDecodeTaskQueue; + params.mImageContainer = new layers::ImageContainer(); + params.mRate = CreateDecoderParams::VideoFrameRate(aFramerate); + params.mOptions = aOptions; + MediaResult error(NS_OK); + params.mError = &error; + +#ifdef MOZ_AV1 + if (AOMDecoder::IsAV1(params.mConfig.mMimeType)) { + mDecoder = new AOMDecoder(params); + } +#endif + + if (NS_FAILED(error)) { + MOZ_ASSERT(aErrorDescription); + *aErrorDescription = error.Description(); + } + + *aSuccess = !!mDecoder; +} + +RemoteVideoDecoderParent::~RemoteVideoDecoderParent() +{ + MOZ_COUNT_DTOR(RemoteVideoDecoderParent); +} + +void +RemoteVideoDecoderParent::Destroy() +{ + MOZ_ASSERT(OnManagerThread()); + mDestroyed = true; + mIPDLSelfRef = nullptr; +} + +mozilla::ipc::IPCResult +RemoteVideoDecoderParent::RecvInit() +{ + MOZ_ASSERT(OnManagerThread()); + RefPtr self = this; + mDecoder->Init()->Then(mManagerTaskQueue, __func__, + [self] (TrackInfo::TrackType aTrack) { + MOZ_ASSERT(aTrack == TrackInfo::kVideoTrack); + if (self->mDecoder) { + Unused << self->SendInitComplete(self->mDecoder->GetDescriptionName(), + self->mDecoder->NeedsConversion()); + } + }, + [self] (MediaResult aReason) { + if (!self->mDestroyed) { + Unused << self->SendInitFailed(aReason); + } + }); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +RemoteVideoDecoderParent::RecvInput(const MediaRawDataIPDL& aData) +{ + MOZ_ASSERT(OnManagerThread()); + // XXX: This copies the data into a buffer owned by the MediaRawData. Ideally + // we'd just take ownership of the shmem. + RefPtr data = new MediaRawData(aData.buffer().get(), + aData.buffer().Size()); + if (aData.buffer().Size() && !data->Data()) { + // OOM + Error(NS_ERROR_OUT_OF_MEMORY); + return IPC_OK(); + } + data->mOffset = aData.base().offset(); + data->mTime = TimeUnit::FromMicroseconds(aData.base().time()); + data->mTimecode = TimeUnit::FromMicroseconds(aData.base().timecode()); + data->mDuration = TimeUnit::FromMicroseconds(aData.base().duration()); + data->mKeyframe = aData.base().keyframe(); + + DeallocShmem(aData.buffer()); + + RefPtr self = this; + mDecoder->Decode(data)->Then( + mManagerTaskQueue, __func__, + [self, this](const MediaDataDecoder::DecodedData& aResults) { + if (mDestroyed) { + return; + } + ProcessDecodedData(aResults); + Unused << SendInputExhausted(); + }, + [self](const MediaResult& aError) { self->Error(aError); }); + return IPC_OK(); +} + +void +RemoteVideoDecoderParent::ProcessDecodedData( + const MediaDataDecoder::DecodedData& aData) +{ + MOZ_ASSERT(OnManagerThread()); + + for (const auto& data : aData) { + MOZ_ASSERT(data->mType == MediaData::VIDEO_DATA, + "Can only decode videos using RemoteVideoDecoderParent!"); + VideoData* video = static_cast(data.get()); + + MOZ_ASSERT(video->mImage, "Decoded video must output a layer::Image to " + "be used with RemoteVideoDecoderParent"); + + PlanarYCbCrImage* image = + static_cast(video->mImage.get()); + + SurfaceDescriptorBuffer sdBuffer; + Shmem buffer; + if (AllocShmem(image->GetDataSize(), + Shmem::SharedMemory::TYPE_BASIC, &buffer) && + image->GetDataSize() == buffer.Size()) { + sdBuffer.data() = buffer; + image->BuildSurfaceDescriptorBuffer(sdBuffer); + } + + RemoteVideoDataIPDL output( + MediaDataIPDL(data->mOffset, data->mTime.ToMicroseconds(), + data->mTimecode.ToMicroseconds(), + data->mDuration.ToMicroseconds(), + data->mFrames, data->mKeyframe), + video->mDisplay, + image->GetSize(), + sdBuffer, + video->mFrameID); + Unused << SendVideoOutput(output); + } +} + +mozilla::ipc::IPCResult +RemoteVideoDecoderParent::RecvFlush() +{ + MOZ_ASSERT(!mDestroyed); + MOZ_ASSERT(OnManagerThread()); + RefPtr self = this; + mDecoder->Flush()->Then( + mManagerTaskQueue, __func__, + [self]() { + if (!self->mDestroyed) { + Unused << self->SendFlushComplete(); + } + }, + [self](const MediaResult& aError) { self->Error(aError); }); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +RemoteVideoDecoderParent::RecvDrain() +{ + MOZ_ASSERT(!mDestroyed); + MOZ_ASSERT(OnManagerThread()); + RefPtr self = this; + mDecoder->Drain()->Then( + mManagerTaskQueue, __func__, + [self, this](const MediaDataDecoder::DecodedData& aResults) { + if (!mDestroyed) { + ProcessDecodedData(aResults); + Unused << SendDrainComplete(); + } + }, + [self](const MediaResult& aError) { self->Error(aError); }); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +RemoteVideoDecoderParent::RecvShutdown() +{ + MOZ_ASSERT(!mDestroyed); + MOZ_ASSERT(OnManagerThread()); + if (mDecoder) { + mDecoder->Shutdown(); + } + mDecoder = nullptr; + return IPC_OK(); +} + +mozilla::ipc::IPCResult +RemoteVideoDecoderParent::RecvSetSeekThreshold(const int64_t& aTime) +{ + MOZ_ASSERT(!mDestroyed); + MOZ_ASSERT(OnManagerThread()); + mDecoder->SetSeekThreshold(TimeUnit::FromMicroseconds(aTime)); + return IPC_OK(); +} + +void +RemoteVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy) +{ + MOZ_ASSERT(!mDestroyed); + MOZ_ASSERT(OnManagerThread()); + if (mDecoder) { + mDecoder->Shutdown(); + mDecoder = nullptr; + } + if (mDecodeTaskQueue) { + mDecodeTaskQueue->BeginShutdown(); + } +} + +void +RemoteVideoDecoderParent::Error(const MediaResult& aError) +{ + MOZ_ASSERT(OnManagerThread()); + if (!mDestroyed) { + Unused << SendError(aError); + } +} + +bool +RemoteVideoDecoderParent::OnManagerThread() +{ + return mParent->OnManagerThread(); +} + +} // namespace mozilla diff --git a/dom/media/ipc/RemoteVideoDecoderParent.h b/dom/media/ipc/RemoteVideoDecoderParent.h new file mode 100644 index 000000000000..3ab3b2525b9b --- /dev/null +++ b/dom/media/ipc/RemoteVideoDecoderParent.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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_RemoteVideoDecoderParent_h +#define include_dom_media_ipc_RemoteVideoDecoderParent_h +#include "mozilla/PRemoteVideoDecoderParent.h" + +namespace mozilla { + +class RemoteDecoderManagerParent; + +class RemoteVideoDecoderParent final : public PRemoteVideoDecoderParent +{ +public: + // We refcount this class since the task queue can have runnables + // that reference us. + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteVideoDecoderParent) + + RemoteVideoDecoderParent(RemoteDecoderManagerParent* aParent, + const VideoInfo& aVideoInfo, + float aFramerate, + const CreateDecoderParams::OptionSet& aOptions, + TaskQueue* aManagerTaskQueue, + TaskQueue* aDecodeTaskQueue, + bool* aSuccess, + nsCString* aErrorDescription); + + void Destroy(); + + // PRemoteVideoDecoderParent + mozilla::ipc::IPCResult RecvInit() override; + mozilla::ipc::IPCResult RecvInput(const MediaRawDataIPDL& aData) override; + mozilla::ipc::IPCResult RecvFlush() override; + mozilla::ipc::IPCResult RecvDrain() override; + mozilla::ipc::IPCResult RecvShutdown() override; + mozilla::ipc::IPCResult RecvSetSeekThreshold(const int64_t& aTime) override; + + void ActorDestroy(ActorDestroyReason aWhy) override; + +private: + bool OnManagerThread(); + void Error(const MediaResult& aError); + + ~RemoteVideoDecoderParent(); + void ProcessDecodedData(const MediaDataDecoder::DecodedData& aData); + + RefPtr mParent; + RefPtr mIPDLSelfRef; + RefPtr mManagerTaskQueue; + RefPtr mDecodeTaskQueue; + RefPtr mDecoder; + + // Can only be accessed from the manager thread + bool mDestroyed; + VideoInfo mVideoInfo; +}; + +} // namespace mozilla + +#endif // include_dom_media_ipc_RemoteVideoDecoderParent_h diff --git a/dom/media/ipc/moz.build b/dom/media/ipc/moz.build index e0033c951877..b073df6e10f0 100644 --- a/dom/media/ipc/moz.build +++ b/dom/media/ipc/moz.build @@ -7,12 +7,17 @@ IPDL_SOURCES += [ 'PMediaDecoderParams.ipdlh', + 'PRemoteDecoderManager.ipdl', + 'PRemoteVideoDecoder.ipdl', 'PVideoDecoder.ipdl', 'PVideoDecoderManager.ipdl', ] EXPORTS.mozilla += [ 'GpuDecoderModule.h', + 'RemoteDecoderManagerChild.h', + 'RemoteDecoderManagerParent.h', + 'RemoteDecoderModule.h', 'RemoteMediaDataDecoder.h', ] @@ -24,7 +29,12 @@ EXPORTS.mozilla.dom += [ SOURCES += [ 'GpuDecoderModule.cpp', + 'RemoteDecoderManagerChild.cpp', + 'RemoteDecoderManagerParent.cpp', + 'RemoteDecoderModule.cpp', 'RemoteMediaDataDecoder.cpp', + 'RemoteVideoDecoderChild.cpp', + 'RemoteVideoDecoderParent.cpp', 'VideoDecoderChild.cpp', 'VideoDecoderManagerChild.cpp', 'VideoDecoderManagerParent.cpp', diff --git a/dom/media/platforms/PlatformDecoderModule.h b/dom/media/platforms/PlatformDecoderModule.h index dd9d3346784f..4ec74c4f4fb9 100644 --- a/dom/media/platforms/PlatformDecoderModule.h +++ b/dom/media/platforms/PlatformDecoderModule.h @@ -35,6 +35,7 @@ class ImageContainer; class GpuDecoderModule; class MediaDataDecoder; +class RemoteDecoderModule; class TaskQueue; class CDMProxy; @@ -213,6 +214,7 @@ protected: friend class PDMFactory; friend class GpuDecoderModule; friend class EMEDecoderModule; + friend class RemoteDecoderModule; // Indicates if the PlatformDecoderModule supports decoding of aColorDepth. // Should override this method when the platform can support color depth != 8. diff --git a/ipc/ipdl/sync-messages.ini b/ipc/ipdl/sync-messages.ini index 712a15ff789c..2a2fe5f2484e 100644 --- a/ipc/ipdl/sync-messages.ini +++ b/ipc/ipdl/sync-messages.ini @@ -914,6 +914,8 @@ description = description = [PGMPVideoEncoder::NeedShmem] description = +[PRemoteDecoderManager::PRemoteVideoDecoder] +description = See Bug 1505976 - investigate changing to async instead of matching GPU pattern [PVideoDecoderManager::PVideoDecoder] description = [PVideoDecoderManager::Readback]