diff options
Diffstat (limited to 'dom/media/ipc/VideoDecoderChild.cpp')
-rw-r--r-- | dom/media/ipc/VideoDecoderChild.cpp | 275 |
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 |