diff options
Diffstat (limited to 'dom/media/ipc')
-rw-r--r-- | dom/media/ipc/MediaIPCUtils.h | 48 | ||||
-rw-r--r-- | dom/media/ipc/PVideoDecoder.ipdl | 78 | ||||
-rw-r--r-- | dom/media/ipc/PVideoDecoderManager.ipdl | 25 | ||||
-rw-r--r-- | dom/media/ipc/RemoteVideoDecoder.cpp | 176 | ||||
-rw-r--r-- | dom/media/ipc/RemoteVideoDecoder.h | 90 | ||||
-rw-r--r-- | dom/media/ipc/VideoDecoderChild.cpp | 275 | ||||
-rw-r--r-- | dom/media/ipc/VideoDecoderChild.h | 80 | ||||
-rw-r--r-- | dom/media/ipc/VideoDecoderManagerChild.cpp | 267 | ||||
-rw-r--r-- | dom/media/ipc/VideoDecoderManagerChild.h | 96 | ||||
-rw-r--r-- | dom/media/ipc/VideoDecoderManagerParent.cpp | 238 | ||||
-rw-r--r-- | dom/media/ipc/VideoDecoderManagerParent.h | 55 | ||||
-rw-r--r-- | dom/media/ipc/VideoDecoderParent.cpp | 280 | ||||
-rw-r--r-- | dom/media/ipc/VideoDecoderParent.h | 69 | ||||
-rw-r--r-- | dom/media/ipc/moz.build | 31 |
14 files changed, 1808 insertions, 0 deletions
diff --git a/dom/media/ipc/MediaIPCUtils.h b/dom/media/ipc/MediaIPCUtils.h new file mode 100644 index 000000000..14b8ee751 --- /dev/null +++ b/dom/media/ipc/MediaIPCUtils.h @@ -0,0 +1,48 @@ +/* -*- 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 "ipc/IPCMessageUtils.h" +#include "mozilla/GfxMessageUtils.h" +#include "PlatformDecoderModule.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.ImageRect()); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + nsIntRect imageRect; + if (ReadParam(aMsg, aIter, &aResult->mMimeType) && + ReadParam(aMsg, aIter, &aResult->mDisplay) && + ReadParam(aMsg, aIter, &aResult->mStereoMode) && + ReadParam(aMsg, aIter, &aResult->mImage) && + ReadParam(aMsg, aIter, &imageRect)) { + aResult->SetImageRect(imageRect); + return true; + } + return false; + } + }; +} // namespace IPC + +#endif // mozilla_dom_media_MediaIPCUtils_h diff --git a/dom/media/ipc/PVideoDecoder.ipdl b/dom/media/ipc/PVideoDecoder.ipdl new file mode 100644 index 000000000..cc2b803b2 --- /dev/null +++ b/dom/media/ipc/PVideoDecoder.ipdl @@ -0,0 +1,78 @@ +/* -*- 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 PVideoDecoderManager; +include LayersSurfaces; +using VideoInfo from "MediaInfo.h"; +using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h"; +using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h"; + +namespace mozilla { +namespace dom { + +struct MediaDataIPDL +{ + int64_t offset; + int64_t time; + int64_t timecode; + int64_t duration; + uint32_t frames; + bool keyframe; +}; + +struct VideoDataIPDL +{ + MediaDataIPDL base; + IntSize display; + SurfaceDescriptorGPUVideo sd; + int32_t frameID; +}; + +struct MediaRawDataIPDL +{ + MediaDataIPDL base; + Shmem buffer; +}; + +// This protocol provides a way to use MediaDataDecoder/MediaDataDecoderCallback +// across processes. The parent side currently is only implemented to work with +// Window Media Foundation, but can be extended easily to support other backends. +// The child side runs in the content process, and the parent side runs in the +// GPU process. We run a separate IPDL thread for both sides. +async protocol PVideoDecoder +{ + manager PVideoDecoderManager; +parent: + async Init(VideoInfo info, TextureFactoryIdentifier identifier); + + async Input(MediaRawDataIPDL data); + + async Flush(); + async Drain(); + async Shutdown(); + + async SetSeekThreshold(int64_t time); + + async __delete__(); + +child: + + async InitComplete(bool hardware, nsCString hardwareReason); + async InitFailed(nsresult reason); + + // Each output includes 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 Output(VideoDataIPDL data); + async InputExhausted(); + async DrainComplete(); + async Error(nsresult error); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/media/ipc/PVideoDecoderManager.ipdl b/dom/media/ipc/PVideoDecoderManager.ipdl new file mode 100644 index 000000000..340207bc5 --- /dev/null +++ b/dom/media/ipc/PVideoDecoderManager.ipdl @@ -0,0 +1,25 @@ +/* -*- 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 PVideoDecoder; +include LayersSurfaces; +include "mozilla/dom/MediaIPCUtils.h"; + +namespace mozilla { +namespace dom { + +sync protocol PVideoDecoderManager +{ + manages PVideoDecoder; +parent: + async PVideoDecoder(); + + sync Readback(SurfaceDescriptorGPUVideo sd) returns (SurfaceDescriptor aResult); + + async DeallocateSurfaceDescriptorGPUVideo(SurfaceDescriptorGPUVideo sd); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/media/ipc/RemoteVideoDecoder.cpp b/dom/media/ipc/RemoteVideoDecoder.cpp new file mode 100644 index 000000000..c6131bb91 --- /dev/null +++ b/dom/media/ipc/RemoteVideoDecoder.cpp @@ -0,0 +1,176 @@ +/* -*- 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 "MediaPrefs.h" +#include "ImageContainer.h" +#include "mozilla/layers/SynchronousTask.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<VideoDecoderChild> actor = mActor; + VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([actor]() { + MOZ_ASSERT(actor); + actor->DestroyIPDL(); + }), NS_DISPATCH_NORMAL); +} + +RefPtr<MediaDataDecoder::InitPromise> +RemoteVideoDecoder::Init() +{ + MOZ_ASSERT(mCallback->OnReaderTaskQueue()); + return InvokeAsync(VideoDecoderManagerChild::GetManagerAbstractThread(), + this, __func__, &RemoteVideoDecoder::InitInternal); +} + +RefPtr<MediaDataDecoder::InitPromise> +RemoteVideoDecoder::InitInternal() +{ + MOZ_ASSERT(mActor); + MOZ_ASSERT(NS_GetCurrentThread() == VideoDecoderManagerChild::GetManagerThread()); + return mActor->Init(); +} + +void +RemoteVideoDecoder::Input(MediaRawData* aSample) +{ + MOZ_ASSERT(mCallback->OnReaderTaskQueue()); + RefPtr<RemoteVideoDecoder> self = this; + RefPtr<MediaRawData> 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<RemoteVideoDecoder> 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<RemoteVideoDecoder> 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()); + SynchronousTask task("Shutdown"); + RefPtr<RemoteVideoDecoder> self = this; + VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([&]() { + AutoCompleteTask complete(&task); + MOZ_ASSERT(self->mActor); + self->mActor->Shutdown(); + }), NS_DISPATCH_NORMAL); + task.Wait(); +} + +bool +RemoteVideoDecoder::IsHardwareAccelerated(nsACString& aFailureReason) const +{ + MOZ_ASSERT(mCallback->OnReaderTaskQueue()); + return mActor->IsHardwareAccelerated(aFailureReason); +} + +void +RemoteVideoDecoder::SetSeekThreshold(const media::TimeUnit& aTime) +{ + MOZ_ASSERT(mCallback->OnReaderTaskQueue()); + RefPtr<RemoteVideoDecoder> self = this; + media::TimeUnit time = aTime; + VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([=]() { + MOZ_ASSERT(self->mActor); + self->mActor->SetSeekThreshold(time); + }), NS_DISPATCH_NORMAL); + +} + +nsresult +RemoteDecoderModule::Startup() +{ + if (!VideoDecoderManagerChild::GetManagerThread()) { + 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<MediaDataDecoder> +RemoteDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams) +{ + if (!MediaPrefs::PDMUseGPUDecoder() || + !aParams.mKnowsCompositor || + aParams.mKnowsCompositor->GetTextureFactoryIdentifier().mParentProcessType != GeckoProcessType_GPU) { + return mWrapped->CreateVideoDecoder(aParams); + } + + MediaDataDecoderCallback* callback = aParams.mCallback; + MOZ_ASSERT(callback->OnReaderTaskQueue()); + RefPtr<RemoteVideoDecoder> object = new RemoteVideoDecoder(callback); + + VideoInfo info = aParams.VideoConfig(); + + TextureFactoryIdentifier ident = aParams.mKnowsCompositor->GetTextureFactoryIdentifier(); + VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([=]() { + object->mActor->InitIPDL(callback, info, ident); + }), 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 000000000..3c8e86bba --- /dev/null +++ b/dom/media/ipc/RemoteVideoDecoder.h @@ -0,0 +1,90 @@ +/* -*- 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<InitPromise> Init() override; + void Input(MediaRawData* aSample) override; + void Flush() override; + void Drain() override; + void Shutdown() override; + bool IsHardwareAccelerated(nsACString& aFailureReason) const override; + void SetSeekThreshold(const media::TimeUnit& aTime) override; + + const char* GetDescriptionName() const override { return "RemoteVideoDecoder"; } + +private: + explicit RemoteVideoDecoder(MediaDataDecoderCallback* aCallback); + ~RemoteVideoDecoder(); + + RefPtr<InitPromise> 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<VideoDecoderChild> 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) + {} + + nsresult Startup() override; + + bool SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const override; + + ConversionRequired DecoderNeedsConversion( + const TrackInfo& aConfig) const override; + + already_AddRefed<MediaDataDecoder> CreateVideoDecoder( + const CreateDecoderParams& aParams) override; + + already_AddRefed<MediaDataDecoder> CreateAudioDecoder( + const CreateDecoderParams& aParams) override + { + return nullptr; + } + +private: + RefPtr<PlatformDecoderModule> mWrapped; +}; + +} // namespace dom +} // namespace mozilla + +#endif // include_dom_ipc_RemoteVideoDecoder_h diff --git a/dom/media/ipc/VideoDecoderChild.cpp b/dom/media/ipc/VideoDecoderChild.cpp new file mode 100644 index 000000000..d1b1d23fc --- /dev/null +++ b/dom/media/ipc/VideoDecoderChild.cpp @@ -0,0 +1,275 @@ +/* -*- 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 "VideoDecoderChild.h" +#include "VideoDecoderManagerChild.h" +#include "mozilla/layers/TextureClient.h" +#include "base/thread.h" +#include "MediaInfo.h" +#include "ImageContainer.h" +#include "GPUVideoImage.h" + +namespace mozilla { +namespace dom { + +using base::Thread; +using namespace ipc; +using namespace layers; +using namespace gfx; + +VideoDecoderChild::VideoDecoderChild() + : mThread(VideoDecoderManagerChild::GetManagerThread()) + , mCanSend(false) + , mInitialized(false) + , mIsHardwareAccelerated(false) +{ +} + +VideoDecoderChild::~VideoDecoderChild() +{ + AssertOnManagerThread(); + mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); +} + +bool +VideoDecoderChild::RecvOutput(const VideoDataIPDL& aData) +{ + AssertOnManagerThread(); + VideoInfo info(aData.display().width, aData.display().height); + + // The Image here creates a TextureData object that takes ownership + // of the SurfaceDescriptor, and is responsible for making sure that + // it gets deallocated. + RefPtr<Image> image = new GPUVideoImage(GetManager(), aData.sd(), aData.display()); + + RefPtr<VideoData> video = VideoData::CreateFromImage(info, + aData.base().offset(), + aData.base().time(), + aData.base().duration(), + image, + aData.base().keyframe(), + aData.base().timecode(), + IntRect()); + if (mCallback) { + mCallback->Output(video); + } + return true; +} + +bool +VideoDecoderChild::RecvInputExhausted() +{ + AssertOnManagerThread(); + if (mCallback) { + mCallback->InputExhausted(); + } + return true; +} + +bool +VideoDecoderChild::RecvDrainComplete() +{ + AssertOnManagerThread(); + if (mCallback) { + mCallback->DrainComplete(); + } + return true; +} + +bool +VideoDecoderChild::RecvError(const nsresult& aError) +{ + AssertOnManagerThread(); + if (mCallback) { + mCallback->Error(aError); + } + return true; +} + +bool +VideoDecoderChild::RecvInitComplete(const bool& aHardware, const nsCString& aHardwareReason) +{ + AssertOnManagerThread(); + mInitPromise.Resolve(TrackInfo::kVideoTrack, __func__); + mInitialized = true; + mIsHardwareAccelerated = aHardware; + mHardwareAcceleratedReason = aHardwareReason; + return true; +} + +bool +VideoDecoderChild::RecvInitFailed(const nsresult& aReason) +{ + AssertOnManagerThread(); + mInitPromise.Reject(aReason, __func__); + return true; +} + +void +VideoDecoderChild::ActorDestroy(ActorDestroyReason aWhy) +{ + if (aWhy == AbnormalShutdown) { + // Defer reporting an error until we've recreated the manager so that + // it'll be safe for MediaFormatReader to recreate decoders + RefPtr<VideoDecoderChild> ref = this; + GetManager()->RunWhenRecreated(NS_NewRunnableFunction([=]() { + if (ref->mInitialized && ref->mCallback) { + ref->mCallback->Error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER); + } else { + ref->mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER, __func__); + } + })); + } + mCanSend = false; +} + +void +VideoDecoderChild::InitIPDL(MediaDataDecoderCallback* aCallback, + const VideoInfo& aVideoInfo, + const layers::TextureFactoryIdentifier& aIdentifier) +{ + RefPtr<VideoDecoderManagerChild> manager = VideoDecoderManagerChild::GetSingleton(); + // If the manager isn't available, then 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. + if (!manager || !manager->CanSend()) { + return; + } + + mIPDLSelfRef = this; + mCallback = aCallback; + mVideoInfo = aVideoInfo; + mIdentifier = aIdentifier; + if (manager->SendPVideoDecoderConstructor(this)) { + mCanSend = true; + } +} + +void +VideoDecoderChild::DestroyIPDL() +{ + if (mCanSend) { + PVideoDecoderChild::Send__delete__(this); + } +} + +void +VideoDecoderChild::IPDLActorDestroyed() +{ + mIPDLSelfRef = nullptr; +} + +// MediaDataDecoder methods + +RefPtr<MediaDataDecoder::InitPromise> +VideoDecoderChild::Init() +{ + AssertOnManagerThread(); + + if (!mIPDLSelfRef) { + return MediaDataDecoder::InitPromise::CreateAndReject( + NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__); + } + // If we failed to send this, then we'll still resolve the Init promise + // as ActorDestroy handles it. + if (mCanSend) { + SendInit(mVideoInfo, mIdentifier); + } + return mInitPromise.Ensure(__func__); +} + +void +VideoDecoderChild::Input(MediaRawData* aSample) +{ + AssertOnManagerThread(); + if (!mCanSend) { + return; + } + + // 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)) { + mCallback->Error(NS_ERROR_DOM_MEDIA_DECODE_ERR); + return; + } + + memcpy(buffer.get<uint8_t>(), aSample->Data(), aSample->Size()); + + MediaRawDataIPDL sample(MediaDataIPDL(aSample->mOffset, + aSample->mTime, + aSample->mTimecode, + aSample->mDuration, + aSample->mFrames, + aSample->mKeyframe), + buffer); + SendInput(sample); +} + +void +VideoDecoderChild::Flush() +{ + AssertOnManagerThread(); + if (mCanSend) { + SendFlush(); + } +} + +void +VideoDecoderChild::Drain() +{ + AssertOnManagerThread(); + if (mCanSend) { + SendDrain(); + } +} + +void +VideoDecoderChild::Shutdown() +{ + AssertOnManagerThread(); + mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); + if (mCanSend) { + SendShutdown(); + } + mInitialized = false; +} + +bool +VideoDecoderChild::IsHardwareAccelerated(nsACString& aFailureReason) const +{ + aFailureReason = mHardwareAcceleratedReason; + return mIsHardwareAccelerated; +} + +void +VideoDecoderChild::SetSeekThreshold(const media::TimeUnit& aTime) +{ + AssertOnManagerThread(); + if (mCanSend) { + SendSetSeekThreshold(aTime.ToMicroseconds()); + } +} + +void +VideoDecoderChild::AssertOnManagerThread() +{ + MOZ_ASSERT(NS_GetCurrentThread() == mThread); +} + +VideoDecoderManagerChild* +VideoDecoderChild::GetManager() +{ + if (!mCanSend) { + return nullptr; + } + return static_cast<VideoDecoderManagerChild*>(Manager()); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/media/ipc/VideoDecoderChild.h b/dom/media/ipc/VideoDecoderChild.h new file mode 100644 index 000000000..a38e3e31c --- /dev/null +++ b/dom/media/ipc/VideoDecoderChild.h @@ -0,0 +1,80 @@ +/* -*- 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_VideoDecoderChild_h +#define include_dom_ipc_VideoDecoderChild_h + +#include "mozilla/RefPtr.h" +#include "mozilla/dom/PVideoDecoderChild.h" +#include "MediaData.h" +#include "PlatformDecoderModule.h" + +namespace mozilla { +namespace dom { + +class RemoteVideoDecoder; +class RemoteDecoderModule; +class VideoDecoderManagerChild; + +class VideoDecoderChild final : public PVideoDecoderChild +{ +public: + explicit VideoDecoderChild(); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderChild) + + // PVideoDecoderChild + bool RecvOutput(const VideoDataIPDL& aData) override; + bool RecvInputExhausted() override; + bool RecvDrainComplete() override; + bool RecvError(const nsresult& aError) override; + bool RecvInitComplete(const bool& aHardware, const nsCString& aHardwareReason) override; + bool RecvInitFailed(const nsresult& aReason) override; + + void ActorDestroy(ActorDestroyReason aWhy) override; + + RefPtr<MediaDataDecoder::InitPromise> Init(); + void Input(MediaRawData* aSample); + void Flush(); + void Drain(); + void Shutdown(); + bool IsHardwareAccelerated(nsACString& aFailureReason) const; + void SetSeekThreshold(const media::TimeUnit& aTime); + + MOZ_IS_CLASS_INIT + void InitIPDL(MediaDataDecoderCallback* aCallback, + const VideoInfo& aVideoInfo, + const layers::TextureFactoryIdentifier& aIdentifier); + void DestroyIPDL(); + + // Called from IPDL when our actor has been destroyed + void IPDLActorDestroyed(); + + VideoDecoderManagerChild* GetManager(); + +private: + ~VideoDecoderChild(); + + void AssertOnManagerThread(); + + RefPtr<VideoDecoderChild> mIPDLSelfRef; + RefPtr<nsIThread> mThread; + + MediaDataDecoderCallback* mCallback; + + MozPromiseHolder<MediaDataDecoder::InitPromise> mInitPromise; + + VideoInfo mVideoInfo; + layers::TextureFactoryIdentifier mIdentifier; + nsCString mHardwareAcceleratedReason; + bool mCanSend; + bool mInitialized; + bool mIsHardwareAccelerated; +}; + +} // namespace dom +} // namespace mozilla + +#endif // include_dom_ipc_VideoDecoderChild_h diff --git a/dom/media/ipc/VideoDecoderManagerChild.cpp b/dom/media/ipc/VideoDecoderManagerChild.cpp new file mode 100644 index 000000000..e444fae15 --- /dev/null +++ b/dom/media/ipc/VideoDecoderManagerChild.cpp @@ -0,0 +1,267 @@ +/* -*- 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 "VideoDecoderManagerChild.h" +#include "VideoDecoderChild.h" +#include "mozilla/dom/ContentChild.h" +#include "MediaPrefs.h" +#include "nsThreadUtils.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/layers/SynchronousTask.h" +#include "mozilla/gfx/DataSurfaceHelpers.h" +#include "mozilla/layers/ISurfaceAllocator.h" +#include "base/task.h" + +namespace mozilla { +namespace dom { + +using namespace ipc; +using namespace layers; +using namespace gfx; + +// Only modified on the main-thread +StaticRefPtr<nsIThread> sVideoDecoderChildThread; +StaticRefPtr<AbstractThread> sVideoDecoderChildAbstractThread; + +// Only accessed from sVideoDecoderChildThread +static StaticRefPtr<VideoDecoderManagerChild> sDecoderManager; +static UniquePtr<nsTArray<RefPtr<Runnable>>> sRecreateTasks; + +/* static */ void +VideoDecoderManagerChild::InitializeThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!sVideoDecoderChildThread) { + RefPtr<nsIThread> childThread; + nsresult rv = NS_NewNamedThread("VideoChild", getter_AddRefs(childThread)); + NS_ENSURE_SUCCESS_VOID(rv); + sVideoDecoderChildThread = childThread; + + sVideoDecoderChildAbstractThread = + AbstractThread::CreateXPCOMThreadWrapper(childThread, false); + + sRecreateTasks = MakeUnique<nsTArray<RefPtr<Runnable>>>(); + } +} + +/* static */ void +VideoDecoderManagerChild::InitForContent(Endpoint<PVideoDecoderManagerChild>&& aVideoManager) +{ + InitializeThread(); + sVideoDecoderChildThread->Dispatch(NewRunnableFunction(&Open, Move(aVideoManager)), NS_DISPATCH_NORMAL); +} + +/* static */ void +VideoDecoderManagerChild::Shutdown() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (sVideoDecoderChildThread) { + sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([]() { + if (sDecoderManager && sDecoderManager->CanSend()) { + sDecoderManager->Close(); + sDecoderManager = nullptr; + } + }), NS_DISPATCH_NORMAL); + + sVideoDecoderChildAbstractThread = nullptr; + sVideoDecoderChildThread->Shutdown(); + sVideoDecoderChildThread = nullptr; + + sRecreateTasks = nullptr; + } +} + +void +VideoDecoderManagerChild::RunWhenRecreated(already_AddRefed<Runnable> aTask) +{ + MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread()); + + // If we've already been recreated, then run the task immediately. + if (sDecoderManager && sDecoderManager != this && sDecoderManager->CanSend()) { + RefPtr<Runnable> task = aTask; + task->Run(); + } else { + sRecreateTasks->AppendElement(aTask); + } +} + + +/* static */ VideoDecoderManagerChild* +VideoDecoderManagerChild::GetSingleton() +{ + MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread()); + return sDecoderManager; +} + +/* static */ nsIThread* +VideoDecoderManagerChild::GetManagerThread() +{ + return sVideoDecoderChildThread; +} + +/* static */ AbstractThread* +VideoDecoderManagerChild::GetManagerAbstractThread() +{ + return sVideoDecoderChildAbstractThread; +} + +PVideoDecoderChild* +VideoDecoderManagerChild::AllocPVideoDecoderChild() +{ + return new VideoDecoderChild(); +} + +bool +VideoDecoderManagerChild::DeallocPVideoDecoderChild(PVideoDecoderChild* actor) +{ + VideoDecoderChild* child = static_cast<VideoDecoderChild*>(actor); + child->IPDLActorDestroyed(); + return true; +} + +void +VideoDecoderManagerChild::Open(Endpoint<PVideoDecoderManagerChild>&& aEndpoint) +{ + // 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. + sDecoderManager = nullptr; + if (aEndpoint.IsValid()) { + RefPtr<VideoDecoderManagerChild> manager = new VideoDecoderManagerChild(); + if (aEndpoint.Bind(manager)) { + sDecoderManager = manager; + manager->InitIPDL(); + } + } + for (Runnable* task : *sRecreateTasks) { + task->Run(); + } + sRecreateTasks->Clear(); +} + +void +VideoDecoderManagerChild::InitIPDL() +{ + mCanSend = true; + mIPDLSelfRef = this; +} + +void +VideoDecoderManagerChild::ActorDestroy(ActorDestroyReason aWhy) +{ + mCanSend = false; +} + +void +VideoDecoderManagerChild::DeallocPVideoDecoderManagerChild() +{ + mIPDLSelfRef = nullptr; +} + +bool +VideoDecoderManagerChild::CanSend() +{ + MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread()); + return mCanSend; +} + +bool +VideoDecoderManagerChild::DeallocShmem(mozilla::ipc::Shmem& aShmem) +{ + if (NS_GetCurrentThread() != sVideoDecoderChildThread) { + RefPtr<VideoDecoderManagerChild> self = this; + mozilla::ipc::Shmem shmem = aShmem; + sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([self, shmem]() { + if (self->CanSend()) { + mozilla::ipc::Shmem shmemCopy = shmem; + self->DeallocShmem(shmemCopy); + } + }), NS_DISPATCH_NORMAL); + return true; + } + return PVideoDecoderManagerChild::DeallocShmem(aShmem); +} + +struct SurfaceDescriptorUserData +{ + SurfaceDescriptorUserData(VideoDecoderManagerChild* aAllocator, SurfaceDescriptor& aSD) + : mAllocator(aAllocator) + , mSD(aSD) + {} + ~SurfaceDescriptorUserData() + { + DestroySurfaceDescriptor(mAllocator, &mSD); + } + + RefPtr<VideoDecoderManagerChild> mAllocator; + SurfaceDescriptor mSD; +}; + +void DeleteSurfaceDescriptorUserData(void* aClosure) +{ + SurfaceDescriptorUserData* sd = reinterpret_cast<SurfaceDescriptorUserData*>(aClosure); + delete sd; +} + +already_AddRefed<SourceSurface> +VideoDecoderManagerChild::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. + SynchronousTask task("Readback sync"); + + RefPtr<VideoDecoderManagerChild> ref = this; + SurfaceDescriptor sd; + sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([&]() { + AutoCompleteTask complete(&task); + if (ref->CanSend()) { + ref->SendReadback(aSD, &sd); + } + }), NS_DISPATCH_NORMAL); + + task.Wait(); + + 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 +VideoDecoderManagerChild::DeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD) +{ + RefPtr<VideoDecoderManagerChild> ref = this; + SurfaceDescriptorGPUVideo sd = Move(aSD); + sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([ref, sd]() { + if (ref->CanSend()) { + ref->SendDeallocateSurfaceDescriptorGPUVideo(sd); + } + }), NS_DISPATCH_NORMAL); +} + +void +VideoDecoderManagerChild::HandleFatalError(const char* aName, const char* aMsg) const +{ + dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid()); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/media/ipc/VideoDecoderManagerChild.h b/dom/media/ipc/VideoDecoderManagerChild.h new file mode 100644 index 000000000..1e67a590e --- /dev/null +++ b/dom/media/ipc/VideoDecoderManagerChild.h @@ -0,0 +1,96 @@ +/* -*- 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_VideoDecoderManagerChild_h +#define include_dom_ipc_VideoDecoderManagerChild_h + +#include "mozilla/RefPtr.h" +#include "mozilla/dom/PVideoDecoderManagerChild.h" + +namespace mozilla { +namespace gfx { +class SourceSurface; +} +namespace dom { + +class VideoDecoderManagerChild final : public PVideoDecoderManagerChild + , public mozilla::ipc::IShmemAllocator +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderManagerChild) + + // Can only be called from the manager thread + static VideoDecoderManagerChild* GetSingleton(); + + // Can be called from any thread. + static nsIThread* GetManagerThread(); + static AbstractThread* GetManagerAbstractThread(); + + // 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); + void DeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD); + + bool AllocShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aShmType, + mozilla::ipc::Shmem* aShmem) override + { + return PVideoDecoderManagerChild::AllocShmem(aSize, aShmType, aShmem); + } + bool AllocUnsafeShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aShmType, + mozilla::ipc::Shmem* aShmem) override + { + return PVideoDecoderManagerChild::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 InitForContent(Endpoint<PVideoDecoderManagerChild>&& 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 RunWhenRecreated(already_AddRefed<Runnable> aTask); + + bool CanSend(); + +protected: + void InitIPDL(); + + void ActorDestroy(ActorDestroyReason aWhy) override; + void DeallocPVideoDecoderManagerChild() override; + + void HandleFatalError(const char* aName, const char* aMsg) const override; + + PVideoDecoderChild* AllocPVideoDecoderChild() override; + bool DeallocPVideoDecoderChild(PVideoDecoderChild* actor) override; + +private: + // Main thread only + static void InitializeThread(); + + VideoDecoderManagerChild() + : mCanSend(false) + {} + ~VideoDecoderManagerChild() {} + + static void Open(Endpoint<PVideoDecoderManagerChild>&& aEndpoint); + + RefPtr<VideoDecoderManagerChild> mIPDLSelfRef; + + // Should only ever be accessed on the manager thread. + bool mCanSend; +}; + +} // namespace dom +} // namespace mozilla + +#endif // include_dom_ipc_VideoDecoderManagerChild_h diff --git a/dom/media/ipc/VideoDecoderManagerParent.cpp b/dom/media/ipc/VideoDecoderManagerParent.cpp new file mode 100644 index 000000000..a111b5e53 --- /dev/null +++ b/dom/media/ipc/VideoDecoderManagerParent.cpp @@ -0,0 +1,238 @@ +/* -*- 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 "VideoDecoderManagerParent.h" +#include "VideoDecoderParent.h" +#include "base/thread.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Services.h" +#include "mozilla/Observer.h" +#include "nsIObserverService.h" +#include "nsIObserver.h" +#include "nsIEventTarget.h" +#include "nsThreadUtils.h" +#include "ImageContainer.h" +#include "mozilla/layers/VideoBridgeChild.h" +#include "mozilla/SharedThreadPool.h" +#include "mozilla/layers/ImageDataSerializer.h" +#include "mozilla/SyncRunnable.h" + +#if XP_WIN +#include <objbase.h> +#endif + +namespace mozilla { +namespace dom { + +using base::Thread; +using namespace ipc; +using namespace layers; +using namespace gfx; + +SurfaceDescriptorGPUVideo +VideoDecoderManagerParent::StoreImage(Image* aImage, TextureClient* aTexture) +{ + mImageMap[aTexture->GetSerial()] = aImage; + mTextureMap[aTexture->GetSerial()] = aTexture; + return SurfaceDescriptorGPUVideo(aTexture->GetSerial()); +} + +StaticRefPtr<nsIThread> sVideoDecoderManagerThread; +StaticRefPtr<TaskQueue> sManagerTaskQueue; + +class ManagerThreadShutdownObserver : public nsIObserver +{ + virtual ~ManagerThreadShutdownObserver() {} +public: + ManagerThreadShutdownObserver() {} + + 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); + + VideoDecoderManagerParent::ShutdownThreads(); + return NS_OK; + } +}; +NS_IMPL_ISUPPORTS(ManagerThreadShutdownObserver, nsIObserver); + +void +VideoDecoderManagerParent::StartupThreads() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (sVideoDecoderManagerThread) { + return; + } + + nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); + if (!observerService) { + return; + } + + RefPtr<nsIThread> managerThread; + nsresult rv = NS_NewNamedThread("VideoParent", getter_AddRefs(managerThread)); + if (NS_FAILED(rv)) { + return; + } + sVideoDecoderManagerThread = managerThread; +#if XP_WIN + sVideoDecoderManagerThread->Dispatch(NS_NewRunnableFunction([]() { + HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); + MOZ_ASSERT(hr == S_OK); + }), NS_DISPATCH_NORMAL); +#endif + sVideoDecoderManagerThread->Dispatch(NS_NewRunnableFunction([]() { + layers::VideoBridgeChild::Startup(); + }), NS_DISPATCH_NORMAL); + + sManagerTaskQueue = new TaskQueue(managerThread.forget()); + + ManagerThreadShutdownObserver* obs = new ManagerThreadShutdownObserver(); + observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); +} + +void +VideoDecoderManagerParent::ShutdownThreads() +{ + sManagerTaskQueue->BeginShutdown(); + sManagerTaskQueue->AwaitShutdownAndIdle(); + sManagerTaskQueue = nullptr; + + sVideoDecoderManagerThread->Shutdown(); + sVideoDecoderManagerThread = nullptr; +} + +void +VideoDecoderManagerParent::ShutdownVideoBridge() +{ + if (sVideoDecoderManagerThread) { + RefPtr<Runnable> task = NS_NewRunnableFunction([]() { + VideoBridgeChild::Shutdown(); + }); + SyncRunnable::DispatchToThread(sVideoDecoderManagerThread, task); + } +} + +bool +VideoDecoderManagerParent::OnManagerThread() +{ + return NS_GetCurrentThread() == sVideoDecoderManagerThread; +} + +bool +VideoDecoderManagerParent::CreateForContent(Endpoint<PVideoDecoderManagerParent>&& aEndpoint) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU); + MOZ_ASSERT(NS_IsMainThread()); + + StartupThreads(); + if (!sVideoDecoderManagerThread) { + return false; + } + + RefPtr<VideoDecoderManagerParent> parent = new VideoDecoderManagerParent(); + + RefPtr<Runnable> task = NewRunnableMethod<Endpoint<PVideoDecoderManagerParent>&&>( + parent, &VideoDecoderManagerParent::Open, Move(aEndpoint)); + sVideoDecoderManagerThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL); + return true; +} + +VideoDecoderManagerParent::VideoDecoderManagerParent() +{ + MOZ_COUNT_CTOR(VideoDecoderManagerParent); +} + +VideoDecoderManagerParent::~VideoDecoderManagerParent() +{ + MOZ_COUNT_DTOR(VideoDecoderManagerParent); +} + +PVideoDecoderParent* +VideoDecoderManagerParent::AllocPVideoDecoderParent() +{ + return new VideoDecoderParent(this, sManagerTaskQueue, new TaskQueue(SharedThreadPool::Get(NS_LITERAL_CSTRING("VideoDecoderParent"), 4))); +} + +bool +VideoDecoderManagerParent::DeallocPVideoDecoderParent(PVideoDecoderParent* actor) +{ + VideoDecoderParent* parent = static_cast<VideoDecoderParent*>(actor); + parent->Destroy(); + return true; +} + +void +VideoDecoderManagerParent::Open(Endpoint<PVideoDecoderManagerParent>&& aEndpoint) +{ + if (!aEndpoint.Bind(this)) { + // We can't recover from this. + MOZ_CRASH("Failed to bind VideoDecoderManagerParent to endpoint"); + } + AddRef(); +} + +void +VideoDecoderManagerParent::DeallocPVideoDecoderManagerParent() +{ + Release(); +} + +bool +VideoDecoderManagerParent::RecvReadback(const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult) +{ + RefPtr<Image> image = mImageMap[aSD.handle()]; + if (!image) { + *aResult = null_t(); + return true; + } + + RefPtr<SourceSurface> source = image->GetAsSourceSurface(); + if (!image) { + *aResult = null_t(); + return true; + } + + 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 true; + } + + 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 true; + } + + dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint()); + dt->Flush(); + + *aResult = SurfaceDescriptorBuffer(RGBDescriptor(size, format, true), MemoryOrShmem(buffer)); + return true; +} + +bool +VideoDecoderManagerParent::RecvDeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD) +{ + mImageMap.erase(aSD.handle()); + mTextureMap.erase(aSD.handle()); + return true; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/media/ipc/VideoDecoderManagerParent.h b/dom/media/ipc/VideoDecoderManagerParent.h new file mode 100644 index 000000000..b140c7ce3 --- /dev/null +++ b/dom/media/ipc/VideoDecoderManagerParent.h @@ -0,0 +1,55 @@ +/* -*- 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_VideoDecoderManagerParent_h +#define include_dom_ipc_VideoDecoderManagerParent_h + +#include "mozilla/dom/PVideoDecoderManagerParent.h" + +namespace mozilla { +namespace dom { + +class VideoDecoderManagerParent final : public PVideoDecoderManagerParent +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderManagerParent) + + static bool CreateForContent(Endpoint<PVideoDecoderManagerParent>&& aEndpoint); + + // Can be called from any thread + SurfaceDescriptorGPUVideo StoreImage(layers::Image* aImage, layers::TextureClient* aTexture); + + static void StartupThreads(); + static void ShutdownThreads(); + + static void ShutdownVideoBridge(); + + bool OnManagerThread(); + +protected: + PVideoDecoderParent* AllocPVideoDecoderParent() override; + bool DeallocPVideoDecoderParent(PVideoDecoderParent* actor) override; + + bool RecvReadback(const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult) override; + bool RecvDeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD) override; + + void ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override {} + + void DeallocPVideoDecoderManagerParent() override; + + private: + VideoDecoderManagerParent(); + ~VideoDecoderManagerParent(); + + void Open(Endpoint<PVideoDecoderManagerParent>&& aEndpoint); + + std::map<uint64_t, RefPtr<layers::Image>> mImageMap; + std::map<uint64_t, RefPtr<layers::TextureClient>> mTextureMap; +}; + +} // namespace dom +} // namespace mozilla + +#endif // include_dom_ipc_VideoDecoderManagerParent_h diff --git a/dom/media/ipc/VideoDecoderParent.cpp b/dom/media/ipc/VideoDecoderParent.cpp new file mode 100644 index 000000000..c96edb044 --- /dev/null +++ b/dom/media/ipc/VideoDecoderParent.cpp @@ -0,0 +1,280 @@ +/* -*- 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 "VideoDecoderParent.h" +#include "mozilla/Unused.h" +#include "mozilla/layers/CompositorThread.h" +#include "base/thread.h" +#include "mozilla/layers/TextureClient.h" +#include "mozilla/layers/VideoBridgeChild.h" +#include "mozilla/layers/ImageClient.h" +#include "MediaInfo.h" +#include "VideoDecoderManagerParent.h" +#ifdef XP_WIN +#include "WMFDecoderModule.h" +#endif + +namespace mozilla { +namespace dom { + +using base::Thread; +using namespace ipc; +using namespace layers; +using namespace gfx; + +class KnowsCompositorVideo : public layers::KnowsCompositor +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(KnowsCompositorVideo, override) + + layers::TextureForwarder* GetTextureForwarder() override + { + return VideoBridgeChild::GetSingleton(); + } + layers::LayersIPCActor* GetLayersIPCActor() override + { + return VideoBridgeChild::GetSingleton(); + } +private: + virtual ~KnowsCompositorVideo() {} +}; + +VideoDecoderParent::VideoDecoderParent(VideoDecoderManagerParent* aParent, + TaskQueue* aManagerTaskQueue, + TaskQueue* aDecodeTaskQueue) + : mParent(aParent) + , mManagerTaskQueue(aManagerTaskQueue) + , mDecodeTaskQueue(aDecodeTaskQueue) + , mKnowsCompositor(new KnowsCompositorVideo) + , mDestroyed(false) +{ + MOZ_COUNT_CTOR(VideoDecoderParent); + // 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; +} + +VideoDecoderParent::~VideoDecoderParent() +{ + MOZ_COUNT_DTOR(VideoDecoderParent); +} + +void +VideoDecoderParent::Destroy() +{ + mDecodeTaskQueue->AwaitShutdownAndIdle(); + mDestroyed = true; + mIPDLSelfRef = nullptr; +} + +bool +VideoDecoderParent::RecvInit(const VideoInfo& aInfo, const layers::TextureFactoryIdentifier& aIdentifier) +{ + mKnowsCompositor->IdentifyTextureHost(aIdentifier); + +#ifdef XP_WIN + // TODO: Ideally we wouldn't hardcode the WMF PDM, and we'd use the normal PDM + // factory logic for picking a decoder. + WMFDecoderModule::Init(); + RefPtr<WMFDecoderModule> pdm(new WMFDecoderModule()); + pdm->Startup(); + + CreateDecoderParams params(aInfo); + params.mTaskQueue = mDecodeTaskQueue; + params.mCallback = this; + params.mKnowsCompositor = mKnowsCompositor; + params.mImageContainer = new layers::ImageContainer(); + + mDecoder = pdm->CreateVideoDecoder(params); + if (!mDecoder) { + Unused << SendInitFailed(NS_ERROR_DOM_MEDIA_FATAL_ERR); + return true; + } +#else + MOZ_ASSERT(false, "Can't use RemoteVideoDecoder on non-Windows platforms yet"); +#endif + + RefPtr<VideoDecoderParent> self = this; + mDecoder->Init()->Then(mManagerTaskQueue, __func__, + [self] (TrackInfo::TrackType aTrack) { + if (!self->mDestroyed) { + nsCString hardwareReason; + bool hardwareAccelerated = self->mDecoder->IsHardwareAccelerated(hardwareReason); + Unused << self->SendInitComplete(hardwareAccelerated, hardwareReason); + } + }, + [self] (MediaResult aReason) { + if (!self->mDestroyed) { + Unused << self->SendInitFailed(aReason); + } + }); + return true; +} + +bool +VideoDecoderParent::RecvInput(const MediaRawDataIPDL& aData) +{ + // XXX: This copies the data into a buffer owned by the MediaRawData. Ideally we'd just take ownership + // of the shmem. + RefPtr<MediaRawData> data = new MediaRawData(aData.buffer().get<uint8_t>(), + aData.buffer().Size<uint8_t>()); + if (aData.buffer().Size<uint8_t>() && !data->Data()) { + // OOM + Error(NS_ERROR_OUT_OF_MEMORY); + return true; + } + data->mOffset = aData.base().offset(); + data->mTime = aData.base().time(); + data->mTimecode = aData.base().timecode(); + data->mDuration = aData.base().duration(); + data->mKeyframe = aData.base().keyframe(); + + DeallocShmem(aData.buffer()); + + mDecoder->Input(data); + return true; +} + +bool +VideoDecoderParent::RecvFlush() +{ + MOZ_ASSERT(!mDestroyed); + if (mDecoder) { + mDecoder->Flush(); + } + return true; +} + +bool +VideoDecoderParent::RecvDrain() +{ + MOZ_ASSERT(!mDestroyed); + mDecoder->Drain(); + return true; +} + +bool +VideoDecoderParent::RecvShutdown() +{ + MOZ_ASSERT(!mDestroyed); + if (mDecoder) { + mDecoder->Shutdown(); + } + mDecoder = nullptr; + return true; +} + +bool +VideoDecoderParent::RecvSetSeekThreshold(const int64_t& aTime) +{ + MOZ_ASSERT(!mDestroyed); + mDecoder->SetSeekThreshold(media::TimeUnit::FromMicroseconds(aTime)); + return true; +} + +void +VideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy) +{ + MOZ_ASSERT(!mDestroyed); + if (mDecoder) { + mDecoder->Shutdown(); + mDecoder = nullptr; + } + if (mDecodeTaskQueue) { + mDecodeTaskQueue->BeginShutdown(); + } +} + +void +VideoDecoderParent::Output(MediaData* aData) +{ + MOZ_ASSERT(mDecodeTaskQueue->IsCurrentThreadIn()); + RefPtr<VideoDecoderParent> self = this; + RefPtr<KnowsCompositor> knowsCompositor = mKnowsCompositor; + RefPtr<MediaData> data = aData; + mManagerTaskQueue->Dispatch(NS_NewRunnableFunction([self, knowsCompositor, data]() { + if (self->mDestroyed) { + return; + } + + MOZ_ASSERT(data->mType == MediaData::VIDEO_DATA, "Can only decode videos using VideoDecoderParent!"); + VideoData* video = static_cast<VideoData*>(data.get()); + + MOZ_ASSERT(video->mImage, "Decoded video must output a layer::Image to be used with VideoDecoderParent"); + + RefPtr<TextureClient> texture = video->mImage->GetTextureClient(knowsCompositor); + + if (!texture) { + texture = ImageClient::CreateTextureClientForImage(video->mImage, knowsCompositor); + } + + if (texture && !texture->IsAddedToCompositableClient()) { + texture->InitIPDLActor(knowsCompositor); + texture->SetAddedToCompositableClient(); + } + + VideoDataIPDL output(MediaDataIPDL(data->mOffset, + data->mTime, + data->mTimecode, + data->mDuration, + data->mFrames, + data->mKeyframe), + video->mDisplay, + texture ? self->mParent->StoreImage(video->mImage, texture) : SurfaceDescriptorGPUVideo(0), + video->mFrameID); + Unused << self->SendOutput(output); + })); +} + +void +VideoDecoderParent::Error(const MediaResult& aError) +{ + MOZ_ASSERT(mDecodeTaskQueue->IsCurrentThreadIn()); + RefPtr<VideoDecoderParent> self = this; + MediaResult error = aError; + mManagerTaskQueue->Dispatch(NS_NewRunnableFunction([self, error]() { + if (!self->mDestroyed) { + Unused << self->SendError(error); + } + })); +} + +void +VideoDecoderParent::InputExhausted() +{ + MOZ_ASSERT(mDecodeTaskQueue->IsCurrentThreadIn()); + RefPtr<VideoDecoderParent> self = this; + mManagerTaskQueue->Dispatch(NS_NewRunnableFunction([self]() { + if (!self->mDestroyed) { + Unused << self->SendInputExhausted(); + } + })); +} + +void +VideoDecoderParent::DrainComplete() +{ + MOZ_ASSERT(mDecodeTaskQueue->IsCurrentThreadIn()); + RefPtr<VideoDecoderParent> self = this; + mManagerTaskQueue->Dispatch(NS_NewRunnableFunction([self]() { + if (!self->mDestroyed) { + Unused << self->SendDrainComplete(); + } + })); +} + +bool +VideoDecoderParent::OnReaderTaskQueue() +{ + // Most of our calls into mDecoder come directly from IPDL so are on + // the right thread, but not actually on the task queue. We only ever + // run a single thread, not a pool, so this should work fine. + return mParent->OnManagerThread(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/media/ipc/VideoDecoderParent.h b/dom/media/ipc/VideoDecoderParent.h new file mode 100644 index 000000000..ea99d0a10 --- /dev/null +++ b/dom/media/ipc/VideoDecoderParent.h @@ -0,0 +1,69 @@ +/* -*- 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_VideoDecoderParent_h +#define include_dom_ipc_VideoDecoderParent_h + +#include "mozilla/RefPtr.h" +#include "mozilla/dom/PVideoDecoderParent.h" +#include "mozilla/layers/TextureForwarder.h" +#include "VideoDecoderManagerParent.h" +#include "MediaData.h" +#include "ImageContainer.h" + +namespace mozilla { +namespace dom { + +class KnowsCompositorVideo; + +class VideoDecoderParent final : public PVideoDecoderParent, + public MediaDataDecoderCallback +{ +public: + // We refcount this class since the task queue can have runnables + // that reference us. + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderParent) + + VideoDecoderParent(VideoDecoderManagerParent* aParent, + TaskQueue* aManagerTaskQueue, + TaskQueue* aDecodeTaskQueue); + + void Destroy(); + + // PVideoDecoderParent + bool RecvInit(const VideoInfo& aVideoInfo, const layers::TextureFactoryIdentifier& aIdentifier) override; + bool RecvInput(const MediaRawDataIPDL& aData) override; + bool RecvFlush() override; + bool RecvDrain() override; + bool RecvShutdown() override; + bool RecvSetSeekThreshold(const int64_t& aTime) override; + + void ActorDestroy(ActorDestroyReason aWhy) override; + + // MediaDataDecoderCallback + void Output(MediaData* aData) override; + void Error(const MediaResult& aError) override; + void InputExhausted() override; + void DrainComplete() override; + bool OnReaderTaskQueue() override; + +private: + ~VideoDecoderParent(); + + RefPtr<VideoDecoderManagerParent> mParent; + RefPtr<VideoDecoderParent> mIPDLSelfRef; + RefPtr<TaskQueue> mManagerTaskQueue; + RefPtr<TaskQueue> mDecodeTaskQueue; + RefPtr<MediaDataDecoder> mDecoder; + RefPtr<KnowsCompositorVideo> mKnowsCompositor; + + // Can only be accessed from the manager thread + bool mDestroyed; +}; + +} // namespace dom +} // namespace mozilla + +#endif // include_dom_ipc_VideoDecoderParent_h diff --git a/dom/media/ipc/moz.build b/dom/media/ipc/moz.build new file mode 100644 index 000000000..2e78460b5 --- /dev/null +++ b/dom/media/ipc/moz.build @@ -0,0 +1,31 @@ +# -*- 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 += [ + 'PVideoDecoder.ipdl', + 'PVideoDecoderManager.ipdl', +] + +EXPORTS.mozilla.dom += [ + 'MediaIPCUtils.h', + 'RemoteVideoDecoder.h', + 'VideoDecoderManagerChild.h', + 'VideoDecoderManagerParent.h', +] + +SOURCES += [ + 'RemoteVideoDecoder.cpp', + 'VideoDecoderChild.cpp', + 'VideoDecoderManagerChild.cpp', + 'VideoDecoderManagerParent.cpp', + 'VideoDecoderParent.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + + +FINAL_LIBRARY = 'xul' |