1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "VideoDecoderManagerParent.h"
7 #include "VideoDecoderParent.h"
8 #include "base/thread.h"
9 #include "mozilla/UniquePtr.h"
10 #include "mozilla/Services.h"
11 #include "mozilla/Observer.h"
12 #include "nsIObserverService.h"
13 #include "nsIObserver.h"
14 #include "nsIEventTarget.h"
15 #include "nsThreadUtils.h"
16 #include "ImageContainer.h"
17 #include "mozilla/layers/VideoBridgeChild.h"
18 #include "mozilla/SharedThreadPool.h"
19 #include "mozilla/layers/ImageDataSerializer.h"
20 #include "mozilla/SyncRunnable.h"
21 
22 #if XP_WIN
23 #include <objbase.h>
24 #endif
25 
26 namespace mozilla {
27 namespace dom {
28 
29 using base::Thread;
30 using namespace ipc;
31 using namespace layers;
32 using namespace gfx;
33 
34 SurfaceDescriptorGPUVideo
StoreImage(Image * aImage,TextureClient * aTexture)35 VideoDecoderManagerParent::StoreImage(Image* aImage, TextureClient* aTexture)
36 {
37   mImageMap[aTexture->GetSerial()] = aImage;
38   mTextureMap[aTexture->GetSerial()] = aTexture;
39   return SurfaceDescriptorGPUVideo(aTexture->GetSerial());
40 }
41 
42 StaticRefPtr<nsIThread> sVideoDecoderManagerThread;
43 StaticRefPtr<TaskQueue> sManagerTaskQueue;
44 
45 class ManagerThreadShutdownObserver : public nsIObserver
46 {
~ManagerThreadShutdownObserver()47   virtual ~ManagerThreadShutdownObserver() {}
48 public:
ManagerThreadShutdownObserver()49   ManagerThreadShutdownObserver() {}
50 
51   NS_DECL_ISUPPORTS
52 
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)53   NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
54                      const char16_t* aData) override
55   {
56     MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
57 
58     VideoDecoderManagerParent::ShutdownThreads();
59     return NS_OK;
60   }
61 };
62 NS_IMPL_ISUPPORTS(ManagerThreadShutdownObserver, nsIObserver);
63 
64 void
StartupThreads()65 VideoDecoderManagerParent::StartupThreads()
66 {
67   MOZ_ASSERT(NS_IsMainThread());
68 
69   if (sVideoDecoderManagerThread) {
70     return;
71   }
72 
73   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
74   if (!observerService) {
75     return;
76   }
77 
78   RefPtr<nsIThread> managerThread;
79   nsresult rv = NS_NewNamedThread("VideoParent", getter_AddRefs(managerThread));
80   if (NS_FAILED(rv)) {
81     return;
82   }
83   sVideoDecoderManagerThread = managerThread;
84 #if XP_WIN
85   sVideoDecoderManagerThread->Dispatch(NS_NewRunnableFunction([]() {
86     HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
87     MOZ_ASSERT(hr == S_OK);
88   }), NS_DISPATCH_NORMAL);
89 #endif
90   sVideoDecoderManagerThread->Dispatch(NS_NewRunnableFunction([]() {
91     layers::VideoBridgeChild::Startup();
92   }), NS_DISPATCH_NORMAL);
93 
94   sManagerTaskQueue = new TaskQueue(managerThread.forget());
95 
96   ManagerThreadShutdownObserver* obs = new ManagerThreadShutdownObserver();
97   observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
98 }
99 
100 void
ShutdownThreads()101 VideoDecoderManagerParent::ShutdownThreads()
102 {
103   sManagerTaskQueue->BeginShutdown();
104   sManagerTaskQueue->AwaitShutdownAndIdle();
105   sManagerTaskQueue = nullptr;
106 
107   sVideoDecoderManagerThread->Shutdown();
108   sVideoDecoderManagerThread = nullptr;
109 }
110 
111 void
ShutdownVideoBridge()112 VideoDecoderManagerParent::ShutdownVideoBridge()
113 {
114   if (sVideoDecoderManagerThread) {
115     RefPtr<Runnable> task = NS_NewRunnableFunction([]() {
116       VideoBridgeChild::Shutdown();
117     });
118     SyncRunnable::DispatchToThread(sVideoDecoderManagerThread, task);
119   }
120 }
121 
122 bool
OnManagerThread()123 VideoDecoderManagerParent::OnManagerThread()
124 {
125   return NS_GetCurrentThread() == sVideoDecoderManagerThread;
126 }
127 
128 bool
CreateForContent(Endpoint<PVideoDecoderManagerParent> && aEndpoint)129 VideoDecoderManagerParent::CreateForContent(Endpoint<PVideoDecoderManagerParent>&& aEndpoint)
130 {
131   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
132   MOZ_ASSERT(NS_IsMainThread());
133 
134   StartupThreads();
135   if (!sVideoDecoderManagerThread) {
136     return false;
137   }
138 
139   RefPtr<VideoDecoderManagerParent> parent = new VideoDecoderManagerParent();
140 
141   RefPtr<Runnable> task = NewRunnableMethod<Endpoint<PVideoDecoderManagerParent>&&>(
142     parent, &VideoDecoderManagerParent::Open, Move(aEndpoint));
143   sVideoDecoderManagerThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
144   return true;
145 }
146 
VideoDecoderManagerParent()147 VideoDecoderManagerParent::VideoDecoderManagerParent()
148 {
149   MOZ_COUNT_CTOR(VideoDecoderManagerParent);
150 }
151 
~VideoDecoderManagerParent()152 VideoDecoderManagerParent::~VideoDecoderManagerParent()
153 {
154   MOZ_COUNT_DTOR(VideoDecoderManagerParent);
155 }
156 
157 PVideoDecoderParent*
AllocPVideoDecoderParent()158 VideoDecoderManagerParent::AllocPVideoDecoderParent()
159 {
160   return new VideoDecoderParent(this, sManagerTaskQueue, new TaskQueue(SharedThreadPool::Get(NS_LITERAL_CSTRING("VideoDecoderParent"), 4)));
161 }
162 
163 bool
DeallocPVideoDecoderParent(PVideoDecoderParent * actor)164 VideoDecoderManagerParent::DeallocPVideoDecoderParent(PVideoDecoderParent* actor)
165 {
166   VideoDecoderParent* parent = static_cast<VideoDecoderParent*>(actor);
167   parent->Destroy();
168   return true;
169 }
170 
171 void
Open(Endpoint<PVideoDecoderManagerParent> && aEndpoint)172 VideoDecoderManagerParent::Open(Endpoint<PVideoDecoderManagerParent>&& aEndpoint)
173 {
174   if (!aEndpoint.Bind(this)) {
175     // We can't recover from this.
176     MOZ_CRASH("Failed to bind VideoDecoderManagerParent to endpoint");
177   }
178   AddRef();
179 }
180 
181 void
DeallocPVideoDecoderManagerParent()182 VideoDecoderManagerParent::DeallocPVideoDecoderManagerParent()
183 {
184   Release();
185 }
186 
187 bool
RecvReadback(const SurfaceDescriptorGPUVideo & aSD,SurfaceDescriptor * aResult)188 VideoDecoderManagerParent::RecvReadback(const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult)
189 {
190   RefPtr<Image> image = mImageMap[aSD.handle()];
191   if (!image) {
192     *aResult = null_t();
193     return true;
194   }
195 
196   RefPtr<SourceSurface> source = image->GetAsSourceSurface();
197   if (!image) {
198     *aResult = null_t();
199     return true;
200   }
201 
202   SurfaceFormat format = source->GetFormat();
203   IntSize size = source->GetSize();
204   size_t length = ImageDataSerializer::ComputeRGBBufferSize(size, format);
205 
206   Shmem buffer;
207   if (!length || !AllocShmem(length, Shmem::SharedMemory::TYPE_BASIC, &buffer)) {
208     *aResult = null_t();
209     return true;
210   }
211 
212   RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(gfx::BackendType::CAIRO,
213                                                            buffer.get<uint8_t>(), size,
214                                                            ImageDataSerializer::ComputeRGBStride(format, size.width),
215                                                            format);
216   if (!dt) {
217     DeallocShmem(buffer);
218     *aResult = null_t();
219     return true;
220   }
221 
222   dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
223   dt->Flush();
224 
225   *aResult = SurfaceDescriptorBuffer(RGBDescriptor(size, format, true), MemoryOrShmem(buffer));
226   return true;
227 }
228 
229 bool
RecvDeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo & aSD)230 VideoDecoderManagerParent::RecvDeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD)
231 {
232   mImageMap.erase(aSD.handle());
233   mTextureMap.erase(aSD.handle());
234   return true;
235 }
236 
237 } // namespace dom
238 } // namespace mozilla
239