summaryrefslogtreecommitdiffstats
path: root/dom/media/ipc/VideoDecoderParent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/ipc/VideoDecoderParent.cpp')
-rw-r--r--dom/media/ipc/VideoDecoderParent.cpp280
1 files changed, 280 insertions, 0 deletions
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