summaryrefslogtreecommitdiffstats
path: root/dom/media/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/ipc')
-rw-r--r--dom/media/ipc/MediaIPCUtils.h48
-rw-r--r--dom/media/ipc/PVideoDecoder.ipdl78
-rw-r--r--dom/media/ipc/PVideoDecoderManager.ipdl25
-rw-r--r--dom/media/ipc/RemoteVideoDecoder.cpp176
-rw-r--r--dom/media/ipc/RemoteVideoDecoder.h90
-rw-r--r--dom/media/ipc/VideoDecoderChild.cpp275
-rw-r--r--dom/media/ipc/VideoDecoderChild.h80
-rw-r--r--dom/media/ipc/VideoDecoderManagerChild.cpp267
-rw-r--r--dom/media/ipc/VideoDecoderManagerChild.h96
-rw-r--r--dom/media/ipc/VideoDecoderManagerParent.cpp238
-rw-r--r--dom/media/ipc/VideoDecoderManagerParent.h55
-rw-r--r--dom/media/ipc/VideoDecoderParent.cpp280
-rw-r--r--dom/media/ipc/VideoDecoderParent.h69
-rw-r--r--dom/media/ipc/moz.build31
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'