diff options
Diffstat (limited to 'dom/media/ipc/VideoDecoderManagerChild.cpp')
-rw-r--r-- | dom/media/ipc/VideoDecoderManagerChild.cpp | 267 |
1 files changed, 267 insertions, 0 deletions
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 |