diff --git a/dom/media/ipc/RemoteVideoDecoder.cpp b/dom/media/ipc/RemoteVideoDecoder.cpp new file mode 100644 index 000000000000..3f30d90a10fe --- /dev/null +++ b/dom/media/ipc/RemoteVideoDecoder.cpp @@ -0,0 +1,145 @@ +/* -*- 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 "RemoteVideoDecoder.h" +#include "VideoDecoderChild.h" +#include "VideoDecoderManagerChild.h" +#include "mozilla/layers/TextureClient.h" +#include "base/thread.h" +#include "MediaInfo.h" +#include "ImageContainer.h" + +namespace mozilla { +namespace dom { + +using base::Thread; +using namespace ipc; +using namespace layers; +using namespace gfx; + +RemoteVideoDecoder::RemoteVideoDecoder(MediaDataDecoderCallback* aCallback) + : mActor(new VideoDecoderChild()) +{ +#ifdef DEBUG + mCallback = aCallback; +#endif + MOZ_ASSERT(mCallback->OnReaderTaskQueue()); +} + +RemoteVideoDecoder::~RemoteVideoDecoder() +{ + // We're about to be destroyed and drop our ref to + // VideoDecoderChild. Make sure we put a ref into the + // task queue for the VideoDecoderChild thread to keep + // it alive until we send the delete message. + RefPtr actor = mActor; + VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([actor]() { + MOZ_ASSERT(actor); + actor->DestroyIPDL(); + }), NS_DISPATCH_NORMAL); +} + +RefPtr +RemoteVideoDecoder::Init() +{ + MOZ_ASSERT(mCallback->OnReaderTaskQueue()); + return InvokeAsync(VideoDecoderManagerChild::GetManagerAbstractThread(), + this, __func__, &RemoteVideoDecoder::InitInternal); +} + +RefPtr +RemoteVideoDecoder::InitInternal() +{ + MOZ_ASSERT(mActor); + MOZ_ASSERT(NS_GetCurrentThread() == VideoDecoderManagerChild::GetManagerThread()); + return mActor->Init(); +} + +void +RemoteVideoDecoder::Input(MediaRawData* aSample) +{ + MOZ_ASSERT(mCallback->OnReaderTaskQueue()); + RefPtr self = this; + RefPtr sample = aSample; + VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([self, sample]() { + MOZ_ASSERT(self->mActor); + self->mActor->Input(sample); + }), NS_DISPATCH_NORMAL); +} + +void +RemoteVideoDecoder::Flush() +{ + MOZ_ASSERT(mCallback->OnReaderTaskQueue()); + RefPtr self = this; + VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([self]() { + MOZ_ASSERT(self->mActor); + self->mActor->Flush(); + }), NS_DISPATCH_NORMAL); +} + +void +RemoteVideoDecoder::Drain() +{ + MOZ_ASSERT(mCallback->OnReaderTaskQueue()); + RefPtr self = this; + VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([self]() { + MOZ_ASSERT(self->mActor); + self->mActor->Drain(); + }), NS_DISPATCH_NORMAL); +} + +void +RemoteVideoDecoder::Shutdown() +{ + MOZ_ASSERT(mCallback->OnReaderTaskQueue()); + RefPtr self = this; + VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([self]() { + MOZ_ASSERT(self->mActor); + self->mActor->Shutdown(); + }), NS_DISPATCH_NORMAL); +} + +nsresult +RemoteDecoderModule::Startup() +{ + if (!VideoDecoderManagerChild::GetSingleton()) { + return NS_ERROR_FAILURE; + } + return mWrapped->Startup(); +} + +bool +RemoteDecoderModule::SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const +{ + return mWrapped->SupportsMimeType(aMimeType, aDiagnostics); +} + +PlatformDecoderModule::ConversionRequired +RemoteDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const +{ + return mWrapped->DecoderNeedsConversion(aConfig); +} + +already_AddRefed +RemoteDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams) +{ + MediaDataDecoderCallback* callback = aParams.mCallback; + MOZ_ASSERT(callback->OnReaderTaskQueue()); + RefPtr object = new RemoteVideoDecoder(callback); + + VideoInfo info = aParams.VideoConfig(); + + layers::LayersBackend backend = aParams.mLayersBackend; + VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([object, callback, info, backend]() { + object->mActor->InitIPDL(callback, info, backend); + }), NS_DISPATCH_NORMAL); + + return object.forget(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/media/ipc/RemoteVideoDecoder.h b/dom/media/ipc/RemoteVideoDecoder.h new file mode 100644 index 000000000000..29d508b2d012 --- /dev/null +++ b/dom/media/ipc/RemoteVideoDecoder.h @@ -0,0 +1,85 @@ +/* -*- 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_ipc_RemoteVideoDecoder_h +#define include_dom_ipc_RemoteVideoDecoder_h + +#include "mozilla/RefPtr.h" +#include "mozilla/DebugOnly.h" +#include "MediaData.h" +#include "PlatformDecoderModule.h" + +namespace mozilla { +namespace dom { + +class VideoDecoderChild; +class RemoteDecoderModule; + +// A MediaDataDecoder implementation that proxies through IPDL +// to a 'real' decoder in the GPU process. +// All requests get forwarded to a VideoDecoderChild instance that +// operates solely on the VideoDecoderManagerChild thread. +class RemoteVideoDecoder : public MediaDataDecoder +{ +public: + friend class RemoteDecoderModule; + + // MediaDataDecoder + RefPtr Init() override; + void Input(MediaRawData* aSample) override; + void Flush() override; + void Drain() override; + void Shutdown() override; + void ConfigurationChanged(const TrackInfo& aConfig) override { MOZ_ASSERT(false); } + + const char* GetDescriptionName() const override { return "RemoteVideoDecoder"; } + +private: + explicit RemoteVideoDecoder(MediaDataDecoderCallback* aCallback); + ~RemoteVideoDecoder(); + + RefPtr InitInternal(); + + // 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 mActor; +#ifdef DEBUG + MediaDataDecoderCallback* mCallback; +#endif +}; + +// A PDM implementation that creates RemoteVideoDecoders. +// We currently require a 'wrapped' PDM in order to be able to answer SupportsMimeType +// and DecoderNeedsConversion. Ideally we'd check these over IPDL using the manager +// protocol +class RemoteDecoderModule : public PlatformDecoderModule +{ +public: + explicit RemoteDecoderModule(PlatformDecoderModule* aWrapped) + : mWrapped(aWrapped) + {} + + virtual nsresult Startup() override; + + virtual bool SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const override; + + virtual ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override; + + virtual already_AddRefed + CreateVideoDecoder(const CreateDecoderParams& aParams) override; + + virtual already_AddRefed + CreateAudioDecoder(const CreateDecoderParams& aParams) override { return nullptr; } + +private: + RefPtr mWrapped; +}; + +} // namespace dom +} // namespace mozilla + +#endif // include_dom_ipc_RemoteVideoDecoder_h diff --git a/dom/media/ipc/VideoDecoderManagerChild.cpp b/dom/media/ipc/VideoDecoderManagerChild.cpp index 01cff6b88513..9934e39cc217 100644 --- a/dom/media/ipc/VideoDecoderManagerChild.cpp +++ b/dom/media/ipc/VideoDecoderManagerChild.cpp @@ -17,6 +17,7 @@ using namespace layers; using namespace gfx; StaticRefPtr sVideoDecoderChildThread; +StaticRefPtr sVideoDecoderChildAbstractThread; static StaticRefPtr sDecoderManager; /* static */ void @@ -41,6 +42,9 @@ VideoDecoderManagerChild::Initialize() nsresult rv = NS_NewNamedThread("VideoChild", getter_AddRefs(childThread)); NS_ENSURE_SUCCESS_VOID(rv); sVideoDecoderChildThread = childThread; + + sVideoDecoderChildAbstractThread = + AbstractThread::CreateXPCOMThreadWrapper(childThread, false); } Endpoint endpoint; @@ -77,6 +81,7 @@ VideoDecoderManagerChild::Shutdown() sDecoderManager = nullptr; + sVideoDecoderChildAbstractThread = nullptr; sVideoDecoderChildThread->Shutdown(); sVideoDecoderChildThread = nullptr; } @@ -94,6 +99,12 @@ VideoDecoderManagerChild::GetManagerThread() return sVideoDecoderChildThread; } +/* static */ AbstractThread* +VideoDecoderManagerChild::GetManagerAbstractThread() +{ + return sVideoDecoderChildAbstractThread; +} + PVideoDecoderChild* VideoDecoderManagerChild::AllocPVideoDecoderChild() { diff --git a/dom/media/ipc/VideoDecoderManagerChild.h b/dom/media/ipc/VideoDecoderManagerChild.h index 40daa514eee0..dc594ce7f5a4 100644 --- a/dom/media/ipc/VideoDecoderManagerChild.h +++ b/dom/media/ipc/VideoDecoderManagerChild.h @@ -19,6 +19,7 @@ public: static VideoDecoderManagerChild* GetSingleton(); static nsIThread* GetManagerThread(); + static AbstractThread* GetManagerAbstractThread(); // Can be called from any thread, dispatches the request to the IPDL thread internally. void DeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD); diff --git a/dom/media/ipc/moz.build b/dom/media/ipc/moz.build index 8ce841ca8c17..2e78460b581c 100644 --- a/dom/media/ipc/moz.build +++ b/dom/media/ipc/moz.build @@ -12,11 +12,13 @@ IPDL_SOURCES += [ EXPORTS.mozilla.dom += [ 'MediaIPCUtils.h', + 'RemoteVideoDecoder.h', 'VideoDecoderManagerChild.h', 'VideoDecoderManagerParent.h', ] SOURCES += [ + 'RemoteVideoDecoder.cpp', 'VideoDecoderChild.cpp', 'VideoDecoderManagerChild.cpp', 'VideoDecoderManagerParent.cpp', diff --git a/dom/media/platforms/PDMFactory.cpp b/dom/media/platforms/PDMFactory.cpp index 5bd783cb74af..375c78e0e15a 100644 --- a/dom/media/platforms/PDMFactory.cpp +++ b/dom/media/platforms/PDMFactory.cpp @@ -47,6 +47,7 @@ #include "DecoderDoctorDiagnostics.h" #include "MP4Decoder.h" +#include "mozilla/dom/RemoteVideoDecoder.h" #include "mp4_demuxer/H264.h" @@ -322,7 +323,11 @@ PDMFactory::CreatePDMs() #ifdef XP_WIN if (MediaPrefs::PDMWMFEnabled()) { m = new WMFDecoderModule(); - mWMFFailedToLoad = !StartupPDM(m); + RefPtr remote = new dom::RemoteDecoderModule(m); + mWMFFailedToLoad = !StartupPDM(remote); + if (mWMFFailedToLoad) { + mWMFFailedToLoad = !StartupPDM(m); + } } else { mWMFFailedToLoad = MediaPrefs::DecoderDoctorWMFDisabledIsFailure(); }