/* -*- 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/UniquePtr.h" #include "mozilla/Services.h" #include "mozilla/Observer.h" #include "nsIObserverService.h" #include "nsIObserver.h" #include "nsIEventTarget.h" #include "nsThreadUtils.h" #include "ImageContainer.h" #include "mozilla/layers/VideoBridgeChild.h" #include "mozilla/SharedThreadPool.h" #include "mozilla/layers/ImageDataSerializer.h" #include "mozilla/SyncRunnable.h" #if XP_WIN #include #endif namespace mozilla { namespace dom { using base::Thread; using namespace ipc; using namespace layers; using namespace gfx; SurfaceDescriptorGPUVideo VideoDecoderManagerParent::StoreImage(Image* aImage, TextureClient* aTexture) { mImageMap[aTexture->GetSerial()] = aImage; mTextureMap[aTexture->GetSerial()] = aTexture; return SurfaceDescriptorGPUVideo(aTexture->GetSerial()); } StaticRefPtr sVideoDecoderManagerThread; StaticRefPtr sManagerTaskQueue; class ManagerThreadShutdownObserver : public nsIObserver { virtual ~ManagerThreadShutdownObserver() {} public: ManagerThreadShutdownObserver() {} NS_DECL_ISUPPORTS NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) override { MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0); VideoDecoderManagerParent::ShutdownThreads(); return NS_OK; } }; NS_IMPL_ISUPPORTS(ManagerThreadShutdownObserver, nsIObserver); void VideoDecoderManagerParent::StartupThreads() { MOZ_ASSERT(NS_IsMainThread()); if (sVideoDecoderManagerThread) { return; } nsCOMPtr observerService = services::GetObserverService(); if (!observerService) { return; } RefPtr managerThread; nsresult rv = NS_NewNamedThread("VideoParent", getter_AddRefs(managerThread)); if (NS_FAILED(rv)) { return; } sVideoDecoderManagerThread = managerThread; #if XP_WIN sVideoDecoderManagerThread->Dispatch(NS_NewRunnableFunction([]() { HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); MOZ_ASSERT(hr == S_OK); }), NS_DISPATCH_NORMAL); #endif sVideoDecoderManagerThread->Dispatch(NS_NewRunnableFunction([]() { layers::VideoBridgeChild::Startup(); }), NS_DISPATCH_NORMAL); sManagerTaskQueue = new TaskQueue(managerThread.forget()); ManagerThreadShutdownObserver* obs = new ManagerThreadShutdownObserver(); observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); } void VideoDecoderManagerParent::ShutdownThreads() { sManagerTaskQueue->BeginShutdown(); sManagerTaskQueue->AwaitShutdownAndIdle(); sManagerTaskQueue = nullptr; sVideoDecoderManagerThread->Shutdown(); sVideoDecoderManagerThread = nullptr; } void VideoDecoderManagerParent::ShutdownVideoBridge() { if (sVideoDecoderManagerThread) { RefPtr task = NS_NewRunnableFunction([]() { VideoBridgeChild::Shutdown(); }); SyncRunnable::DispatchToThread(sVideoDecoderManagerThread, task); } } bool VideoDecoderManagerParent::OnManagerThread() { return NS_GetCurrentThread() == sVideoDecoderManagerThread; } bool VideoDecoderManagerParent::CreateForContent(Endpoint&& aEndpoint) { MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU); MOZ_ASSERT(NS_IsMainThread()); StartupThreads(); if (!sVideoDecoderManagerThread) { return false; } RefPtr parent = new VideoDecoderManagerParent(); RefPtr task = NewRunnableMethod&&>( parent, &VideoDecoderManagerParent::Open, Move(aEndpoint)); sVideoDecoderManagerThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL); return true; } VideoDecoderManagerParent::VideoDecoderManagerParent() { MOZ_COUNT_CTOR(VideoDecoderManagerParent); } VideoDecoderManagerParent::~VideoDecoderManagerParent() { MOZ_COUNT_DTOR(VideoDecoderManagerParent); } PVideoDecoderParent* VideoDecoderManagerParent::AllocPVideoDecoderParent() { return new VideoDecoderParent(this, sManagerTaskQueue, new TaskQueue(SharedThreadPool::Get(NS_LITERAL_CSTRING("VideoDecoderParent"), 4))); } bool VideoDecoderManagerParent::DeallocPVideoDecoderParent(PVideoDecoderParent* actor) { VideoDecoderParent* parent = static_cast(actor); parent->Destroy(); return true; } void VideoDecoderManagerParent::Open(Endpoint&& aEndpoint) { if (!aEndpoint.Bind(this)) { // We can't recover from this. MOZ_CRASH("Failed to bind VideoDecoderManagerParent to endpoint"); } AddRef(); } void VideoDecoderManagerParent::DeallocPVideoDecoderManagerParent() { Release(); } bool VideoDecoderManagerParent::RecvReadback(const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult) { RefPtr image = mImageMap[aSD.handle()]; if (!image) { *aResult = null_t(); return true; } RefPtr source = image->GetAsSourceSurface(); if (!source) { *aResult = null_t(); return true; } SurfaceFormat format = source->GetFormat(); IntSize size = source->GetSize(); size_t length = ImageDataSerializer::ComputeRGBBufferSize(size, format); Shmem buffer; if (!length || !AllocShmem(length, Shmem::SharedMemory::TYPE_BASIC, &buffer)) { *aResult = null_t(); return true; } RefPtr dt = Factory::CreateDrawTargetForData(gfx::BackendType::CAIRO, buffer.get(), size, ImageDataSerializer::ComputeRGBStride(format, size.width), format); if (!dt) { DeallocShmem(buffer); *aResult = null_t(); return true; } dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint()); dt->Flush(); *aResult = SurfaceDescriptorBuffer(RGBDescriptor(size, format, true), MemoryOrShmem(buffer)); return true; } bool VideoDecoderManagerParent::RecvDeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD) { mImageMap.erase(aSD.handle()); mTextureMap.erase(aSD.handle()); return true; } } // namespace dom } // namespace mozilla