Bug 1288618 - Part 14: Add PVideoDecoder protocol for individual decoders. r?cpearce,dvander
* * *
[mq]: vdc-fix
MozReview-Commit-ID: 7ARVjyUFmtv
* * *
[mq]: fix
MozReview-Commit-ID: G8tm4H5lNU8
* * *
[mq]: fix
MozReview-Commit-ID: FJvnBouXamK
* * *
[mq]: video-decoder-fixes
MozReview-Commit-ID: 46XZcSjJgvm
new file mode 100644
--- /dev/null
+++ b/dom/media/ipc/PVideoDecoder.ipdl
@@ -0,0 +1,75 @@
+/* -*- 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";
+
+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, LayersBackend backend);
+
+ async Input(MediaRawDataIPDL data);
+
+ async Flush();
+ async Drain();
+ async Shutdown();
+
+ async __delete__();
+
+child:
+
+ async InitComplete();
+ 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
--- a/dom/media/ipc/PVideoDecoderManager.ipdl
+++ b/dom/media/ipc/PVideoDecoderManager.ipdl
@@ -1,20 +1,23 @@
/* -*- 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 {
async protocol PVideoDecoderManager
{
+ manages PVideoDecoder;
parent:
+ async PVideoDecoder();
async DeallocateSurfaceDescriptorGPUVideo(SurfaceDescriptorGPUVideo sd);
};
} // namespace dom
} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/ipc/VideoDecoderChild.cpp
@@ -0,0 +1,208 @@
+/* -*- 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(true)
+{
+}
+
+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(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());
+ mCallback->Output(video);
+ return true;
+}
+
+bool
+VideoDecoderChild::RecvInputExhausted()
+{
+ AssertOnManagerThread();
+ mCallback->InputExhausted();
+ return true;
+}
+
+bool
+VideoDecoderChild::RecvDrainComplete()
+{
+ AssertOnManagerThread();
+ mCallback->DrainComplete();
+ return true;
+}
+
+bool
+VideoDecoderChild::RecvError(const nsresult& aError)
+{
+ AssertOnManagerThread();
+ mCallback->Error(aError);
+ return true;
+}
+
+bool
+VideoDecoderChild::RecvInitComplete()
+{
+ AssertOnManagerThread();
+ mInitPromise.Resolve(TrackInfo::kVideoTrack, __func__);
+ return true;
+}
+
+bool
+VideoDecoderChild::RecvInitFailed(const nsresult& aReason)
+{
+ AssertOnManagerThread();
+ mInitPromise.Reject(aReason, __func__);
+ return true;
+}
+
+void
+VideoDecoderChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ mCanSend = false;
+}
+
+void
+VideoDecoderChild::InitIPDL(MediaDataDecoderCallback* aCallback,
+ const VideoInfo& aVideoInfo,
+ layers::LayersBackend aLayersBackend)
+{
+ VideoDecoderManagerChild::GetSingleton()->SendPVideoDecoderConstructor(this);
+ mIPDLSelfRef = this;
+ mCallback = aCallback;
+ mVideoInfo = aVideoInfo;
+ mLayersBackend = aLayersBackend;
+}
+
+void
+VideoDecoderChild::DestroyIPDL()
+{
+ if (mCanSend) {
+ PVideoDecoderChild::Send__delete__(this);
+ }
+}
+
+void
+VideoDecoderChild::IPDLActorDestroyed()
+{
+ mIPDLSelfRef = nullptr;
+}
+
+// MediaDataDecoder methods
+
+RefPtr<MediaDataDecoder::InitPromise>
+VideoDecoderChild::Init()
+{
+ AssertOnManagerThread();
+ if (!mCanSend || !SendInit(mVideoInfo, mLayersBackend)) {
+ return MediaDataDecoder::InitPromise::CreateAndReject(
+ NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
+ }
+ return mInitPromise.Ensure(__func__);
+}
+
+void
+VideoDecoderChild::Input(MediaRawData* aSample)
+{
+ AssertOnManagerThread();
+ if (!mCanSend) {
+ mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+ 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_FATAL_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);
+ if (!SendInput(sample)) {
+ mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+ }
+}
+
+void
+VideoDecoderChild::Flush()
+{
+ AssertOnManagerThread();
+ if (!mCanSend || !SendFlush()) {
+ mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+ }
+}
+
+void
+VideoDecoderChild::Drain()
+{
+ AssertOnManagerThread();
+ if (!mCanSend || !SendDrain()) {
+ mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+ }
+}
+
+void
+VideoDecoderChild::Shutdown()
+{
+ AssertOnManagerThread();
+ if (!mCanSend || !SendShutdown()) {
+ mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+ }
+}
+
+void
+VideoDecoderChild::AssertOnManagerThread()
+{
+ MOZ_ASSERT(NS_GetCurrentThread() == mThread);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/ipc/VideoDecoderChild.h
@@ -0,0 +1,71 @@
+/* -*- 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 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() 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();
+
+ void InitIPDL(MediaDataDecoderCallback* aCallback,
+ const VideoInfo& aVideoInfo,
+ layers::LayersBackend aLayersBackend);
+ void DestroyIPDL();
+
+ // Called from IPDL when our actor has been destroyed
+ void IPDLActorDestroyed();
+
+private:
+ ~VideoDecoderChild();
+
+ void AssertOnManagerThread();
+
+ RefPtr<VideoDecoderChild> mIPDLSelfRef;
+ RefPtr<nsIThread> mThread;
+
+ MediaDataDecoderCallback* mCallback;
+
+ MozPromiseHolder<MediaDataDecoder::InitPromise> mInitPromise;
+
+ VideoInfo mVideoInfo;
+ layers::LayersBackend mLayersBackend;
+ bool mCanSend;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // include_dom_ipc_VideoDecoderChild_h
--- a/dom/media/ipc/VideoDecoderManagerChild.cpp
+++ b/dom/media/ipc/VideoDecoderManagerChild.cpp
@@ -1,14 +1,15 @@
/* -*- 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"
namespace mozilla {
namespace dom {
using namespace ipc;
@@ -82,16 +83,36 @@ VideoDecoderManagerChild::Shutdown()
}
/* static */ VideoDecoderManagerChild*
VideoDecoderManagerChild::GetSingleton()
{
return sDecoderManager;
}
+/* static */ nsIThread*
+VideoDecoderManagerChild::GetManagerThread()
+{
+ return sVideoDecoderChildThread;
+}
+
+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)
{
if (!aEndpoint.Bind(this)) {
// We can't recover from this.
MOZ_CRASH("Failed to bind VideoDecoderChild to endpoint");
}
AddRef();
--- a/dom/media/ipc/VideoDecoderManagerChild.h
+++ b/dom/media/ipc/VideoDecoderManagerChild.h
@@ -13,26 +13,31 @@ namespace mozilla {
namespace dom {
class VideoDecoderManagerChild final : public PVideoDecoderManagerChild
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderManagerChild)
static VideoDecoderManagerChild* GetSingleton();
+ static nsIThread* GetManagerThread();
// Can be called from any thread, dispatches the request to the IPDL thread internally.
void DeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD);
void DeallocPVideoDecoderManagerChild() override;
// Main thread only
static void Initialize();
static void Shutdown();
+protected:
+ PVideoDecoderChild* AllocPVideoDecoderChild() override;
+ bool DeallocPVideoDecoderChild(PVideoDecoderChild* actor) override;
+
private:
VideoDecoderManagerChild()
{}
~VideoDecoderManagerChild() {}
void Open(Endpoint<PVideoDecoderManagerChild>&& aEndpoint);
};
--- a/dom/media/ipc/VideoDecoderManagerParent.cpp
+++ b/dom/media/ipc/VideoDecoderManagerParent.cpp
@@ -1,14 +1,15 @@
/* -*- 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/StaticMutex.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Services.h"
#include "mozilla/Observer.h"
#include "nsIObserverService.h"
#include "nsIObserver.h"
#include "nsIEventTarget.h"
@@ -155,16 +156,22 @@ VideoDecoderManagerParent::ShutdownThrea
sManagerTaskQueue->AwaitShutdownAndIdle();
sVideoDecoderTaskThread->Shutdown();
sVideoDecoderTaskThread = nullptr;
sVideoDecoderManagerThread->Shutdown();
sVideoDecoderManagerThread = nullptr;
}
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;
@@ -185,16 +192,31 @@ VideoDecoderManagerParent::VideoDecoderM
VideoDecoderManagerParent::~VideoDecoderManagerParent()
{
MOZ_COUNT_DTOR(VideoDecoderManagerParent);
ClearAllOwnedImages();
}
+PVideoDecoderParent*
+VideoDecoderManagerParent::AllocPVideoDecoderParent()
+{
+ RefPtr<nsIEventTarget> target = sVideoDecoderTaskThread;;
+ return new VideoDecoderParent(this, sManagerTaskQueue, new TaskQueue(target.forget()));
+}
+
+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();
--- a/dom/media/ipc/VideoDecoderManagerParent.h
+++ b/dom/media/ipc/VideoDecoderManagerParent.h
@@ -20,17 +20,22 @@ public:
// Can be called from any thread
static layers::Image* LookupImage(const SurfaceDescriptorGPUVideo& aSD);
SurfaceDescriptorGPUVideo StoreImage(layers::Image* aImage);
static void StartupThreads();
static void ShutdownThreads();
+ bool OnManagerThread();
+
protected:
+ PVideoDecoderParent* AllocPVideoDecoderParent() override;
+ bool DeallocPVideoDecoderParent(PVideoDecoderParent* actor) override;
+
bool RecvDeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD) override;
void ActorDestroy(mozilla::ipc::IProtocolManager<mozilla::ipc::IProtocol>::ActorDestroyReason) override {}
void DeallocPVideoDecoderManagerParent() override;
private:
VideoDecoderManagerParent();
new file mode 100644
--- /dev/null
+++ b/dom/media/ipc/VideoDecoderParent.cpp
@@ -0,0 +1,227 @@
+/* -*- 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/TextureHost.h"
+#include "mozilla/layers/PTextureParent.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;
+
+VideoDecoderParent::VideoDecoderParent(VideoDecoderManagerParent* aParent,
+ TaskQueue* aManagerTaskQueue,
+ TaskQueue* aDecodeTaskQueue)
+ : mParent(aParent)
+ , mManagerTaskQueue(aManagerTaskQueue)
+ , mDecodeTaskQueue(aDecodeTaskQueue)
+ , 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::LayersBackend& aBackend)
+{
+ CreateDecoderParams params(aInfo);
+ params.mTaskQueue = mDecodeTaskQueue;
+ params.mCallback = this;
+ params.mLayersBackend = aBackend;
+ params.mImageContainer = new layers::ImageContainer();
+
+#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();
+
+ 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) {
+ Unused << self->SendInitComplete();
+ }
+ },
+ [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>());
+ 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);
+ mDecoder->Flush();
+ return true;
+}
+
+bool
+VideoDecoderParent::RecvDrain()
+{
+ MOZ_ASSERT(!mDestroyed);
+ mDecoder->Drain();
+ return true;
+}
+
+bool
+VideoDecoderParent::RecvShutdown()
+{
+ MOZ_ASSERT(!mDestroyed);
+ mDecoder->Shutdown();
+ mDecoder = nullptr;
+ 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<MediaData> data = aData;
+ mManagerTaskQueue->Dispatch(NS_NewRunnableFunction([self, 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");
+
+ VideoDataIPDL output(MediaDataIPDL(data->mOffset,
+ data->mTime,
+ data->mTimecode,
+ data->mDuration,
+ data->mFrames,
+ data->mKeyframe),
+ video->mDisplay,
+ self->GetManager()->StoreImage(video->mImage),
+ 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
new file mode 100644
--- /dev/null
+++ b/dom/media/ipc/VideoDecoderParent.h
@@ -0,0 +1,66 @@
+/* -*- 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 "VideoDecoderManagerParent.h"
+#include "MediaData.h"
+#include "ImageContainer.h"
+
+namespace mozilla {
+namespace dom {
+
+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::LayersBackend& aBackend) override;
+ bool RecvInput(const MediaRawDataIPDL& aData) override;
+ bool RecvFlush() override;
+ bool RecvDrain() override;
+ bool RecvShutdown() 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();
+
+ VideoDecoderManagerParent* GetManager() { return static_cast<VideoDecoderManagerParent*>(Manager()); }
+
+ RefPtr<VideoDecoderManagerParent> mParent;
+ RefPtr<VideoDecoderParent> mIPDLSelfRef;
+ RefPtr<TaskQueue> mManagerTaskQueue;
+ RefPtr<TaskQueue> mDecodeTaskQueue;
+ RefPtr<MediaDataDecoder> mDecoder;
+
+ // Can only be accessed from the manager thread
+ bool mDestroyed;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // include_dom_ipc_VideoDecoderParent_h
--- a/dom/media/ipc/moz.build
+++ b/dom/media/ipc/moz.build
@@ -1,26 +1,29 @@
# -*- 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',
'VideoDecoderManagerChild.h',
'VideoDecoderManagerParent.h',
]
SOURCES += [
+ 'VideoDecoderChild.cpp',
'VideoDecoderManagerChild.cpp',
'VideoDecoderManagerParent.cpp',
+ 'VideoDecoderParent.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'