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