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