1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 "RemoteDecoderManagerParent.h"
7
8 #if XP_WIN
9 # include <objbase.h>
10 #endif
11
12 #include "ImageContainer.h"
13 #include "PDMFactory.h"
14 #include "RemoteAudioDecoder.h"
15 #include "RemoteVideoDecoder.h"
16 #include "VideoUtils.h" // for MediaThreadType
17 #include "mozilla/RDDParent.h"
18 #include "mozilla/SyncRunnable.h"
19 #include "mozilla/gfx/GPUParent.h"
20 #include "mozilla/ipc/Endpoint.h"
21 #include "mozilla/layers/ImageDataSerializer.h"
22 #include "mozilla/layers/VideoBridgeChild.h"
23
24 namespace mozilla {
25
26 using namespace ipc;
27 using namespace layers;
28 using namespace gfx;
29
30 StaticRefPtr<TaskQueue> sRemoteDecoderManagerParentThread;
31
StoreImage(const SurfaceDescriptorGPUVideo & aSD,Image * aImage,TextureClient * aTexture)32 void RemoteDecoderManagerParent::StoreImage(
33 const SurfaceDescriptorGPUVideo& aSD, Image* aImage,
34 TextureClient* aTexture) {
35 MOZ_ASSERT(OnManagerThread());
36 mImageMap[static_cast<SurfaceDescriptorRemoteDecoder>(aSD).handle()] = aImage;
37 mTextureMap[static_cast<SurfaceDescriptorRemoteDecoder>(aSD).handle()] =
38 aTexture;
39 }
40
41 class RemoteDecoderManagerThreadShutdownObserver : public nsIObserver {
42 virtual ~RemoteDecoderManagerThreadShutdownObserver() = default;
43
44 public:
45 RemoteDecoderManagerThreadShutdownObserver() = default;
46
47 NS_DECL_ISUPPORTS
48
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)49 NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
50 const char16_t* aData) override {
51 MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
52
53 RemoteDecoderManagerParent::ShutdownVideoBridge();
54 RemoteDecoderManagerParent::ShutdownThreads();
55 return NS_OK;
56 }
57 };
58 NS_IMPL_ISUPPORTS(RemoteDecoderManagerThreadShutdownObserver, nsIObserver);
59
StartupThreads()60 bool RemoteDecoderManagerParent::StartupThreads() {
61 MOZ_ASSERT(NS_IsMainThread());
62
63 if (sRemoteDecoderManagerParentThread) {
64 return true;
65 }
66
67 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
68 if (!observerService) {
69 return false;
70 }
71
72 sRemoteDecoderManagerParentThread = new TaskQueue(
73 GetMediaThreadPool(MediaThreadType::SUPERVISOR), "RemVidParent");
74 if (XRE_IsGPUProcess()) {
75 MOZ_ALWAYS_SUCCEEDS(
76 sRemoteDecoderManagerParentThread->Dispatch(NS_NewRunnableFunction(
77 "RemoteDecoderManagerParent::StartupThreads",
78 []() { layers::VideoBridgeChild::StartupForGPUProcess(); })));
79 }
80
81 auto* obs = new RemoteDecoderManagerThreadShutdownObserver();
82 observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
83 return true;
84 }
85
ShutdownThreads()86 void RemoteDecoderManagerParent::ShutdownThreads() {
87 sRemoteDecoderManagerParentThread->BeginShutdown();
88 sRemoteDecoderManagerParentThread->AwaitShutdownAndIdle();
89 sRemoteDecoderManagerParentThread = nullptr;
90 }
91
92 /* static */
ShutdownVideoBridge()93 void RemoteDecoderManagerParent::ShutdownVideoBridge() {
94 if (sRemoteDecoderManagerParentThread) {
95 RefPtr<Runnable> task = NS_NewRunnableFunction(
96 "RemoteDecoderManagerParent::ShutdownVideoBridge",
97 []() { VideoBridgeChild::Shutdown(); });
98 SyncRunnable::DispatchToThread(sRemoteDecoderManagerParentThread, task);
99 }
100 }
101
OnManagerThread()102 bool RemoteDecoderManagerParent::OnManagerThread() {
103 return sRemoteDecoderManagerParentThread->IsOnCurrentThread();
104 }
105
EnsurePDMFactory()106 PDMFactory& RemoteDecoderManagerParent::EnsurePDMFactory() {
107 MOZ_ASSERT(OnManagerThread());
108 if (!mPDMFactory) {
109 mPDMFactory = MakeRefPtr<PDMFactory>();
110 }
111 return *mPDMFactory;
112 }
113
CreateForContent(Endpoint<PRemoteDecoderManagerParent> && aEndpoint)114 bool RemoteDecoderManagerParent::CreateForContent(
115 Endpoint<PRemoteDecoderManagerParent>&& aEndpoint) {
116 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_RDD ||
117 XRE_GetProcessType() == GeckoProcessType_GPU);
118 MOZ_ASSERT(NS_IsMainThread());
119
120 if (!StartupThreads()) {
121 return false;
122 }
123
124 RefPtr<RemoteDecoderManagerParent> parent =
125 new RemoteDecoderManagerParent(sRemoteDecoderManagerParentThread);
126
127 RefPtr<Runnable> task =
128 NewRunnableMethod<Endpoint<PRemoteDecoderManagerParent>&&>(
129 "dom::RemoteDecoderManagerParent::Open", parent,
130 &RemoteDecoderManagerParent::Open, std::move(aEndpoint));
131 MOZ_ALWAYS_SUCCEEDS(
132 sRemoteDecoderManagerParentThread->Dispatch(task.forget()));
133 return true;
134 }
135
CreateVideoBridgeToOtherProcess(Endpoint<PVideoBridgeChild> && aEndpoint)136 bool RemoteDecoderManagerParent::CreateVideoBridgeToOtherProcess(
137 Endpoint<PVideoBridgeChild>&& aEndpoint) {
138 // We never want to decode in the GPU process, but output
139 // frames to the parent process.
140 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_RDD);
141 MOZ_ASSERT(NS_IsMainThread());
142
143 if (!StartupThreads()) {
144 return false;
145 }
146
147 RefPtr<Runnable> task =
148 NewRunnableFunction("gfx::VideoBridgeChild::Open",
149 &VideoBridgeChild::Open, std::move(aEndpoint));
150 MOZ_ALWAYS_SUCCEEDS(
151 sRemoteDecoderManagerParentThread->Dispatch(task.forget()));
152 return true;
153 }
154
RemoteDecoderManagerParent(nsISerialEventTarget * aThread)155 RemoteDecoderManagerParent::RemoteDecoderManagerParent(
156 nsISerialEventTarget* aThread)
157 : mThread(aThread) {
158 MOZ_COUNT_CTOR(RemoteDecoderManagerParent);
159 auto& registrar = XRE_IsGPUProcess()
160 ? GPUParent::GetSingleton()->AsyncShutdownService()
161 : RDDParent::GetSingleton()->AsyncShutdownService();
162 registrar.Register(this);
163 }
164
~RemoteDecoderManagerParent()165 RemoteDecoderManagerParent::~RemoteDecoderManagerParent() {
166 MOZ_COUNT_DTOR(RemoteDecoderManagerParent);
167 auto& registrar = XRE_IsGPUProcess()
168 ? GPUParent::GetSingleton()->AsyncShutdownService()
169 : RDDParent::GetSingleton()->AsyncShutdownService();
170 registrar.Deregister(this);
171 }
172
ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason)173 void RemoteDecoderManagerParent::ActorDestroy(
174 mozilla::ipc::IProtocol::ActorDestroyReason) {
175 mThread = nullptr;
176 }
177
AllocPRemoteDecoderParent(const RemoteDecoderInfoIPDL & aRemoteDecoderInfo,const CreateDecoderParams::OptionSet & aOptions,const Maybe<layers::TextureFactoryIdentifier> & aIdentifier)178 PRemoteDecoderParent* RemoteDecoderManagerParent::AllocPRemoteDecoderParent(
179 const RemoteDecoderInfoIPDL& aRemoteDecoderInfo,
180 const CreateDecoderParams::OptionSet& aOptions,
181 const Maybe<layers::TextureFactoryIdentifier>& aIdentifier) {
182 RefPtr<TaskQueue> decodeTaskQueue =
183 new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
184 "RemoteVideoDecoderParent::mDecodeTaskQueue");
185
186 if (aRemoteDecoderInfo.type() ==
187 RemoteDecoderInfoIPDL::TVideoDecoderInfoIPDL) {
188 const VideoDecoderInfoIPDL& decoderInfo =
189 aRemoteDecoderInfo.get_VideoDecoderInfoIPDL();
190 return new RemoteVideoDecoderParent(
191 this, decoderInfo.videoInfo(), decoderInfo.framerate(), aOptions,
192 aIdentifier, sRemoteDecoderManagerParentThread, decodeTaskQueue);
193 }
194
195 if (aRemoteDecoderInfo.type() == RemoteDecoderInfoIPDL::TAudioInfo) {
196 return new RemoteAudioDecoderParent(
197 this, aRemoteDecoderInfo.get_AudioInfo(), aOptions,
198 sRemoteDecoderManagerParentThread, decodeTaskQueue);
199 }
200
201 MOZ_CRASH("unrecognized type of RemoteDecoderInfoIPDL union");
202 return nullptr;
203 }
204
DeallocPRemoteDecoderParent(PRemoteDecoderParent * actor)205 bool RemoteDecoderManagerParent::DeallocPRemoteDecoderParent(
206 PRemoteDecoderParent* actor) {
207 RemoteDecoderParent* parent = static_cast<RemoteDecoderParent*>(actor);
208 parent->Destroy();
209 return true;
210 }
211
Open(Endpoint<PRemoteDecoderManagerParent> && aEndpoint)212 void RemoteDecoderManagerParent::Open(
213 Endpoint<PRemoteDecoderManagerParent>&& aEndpoint) {
214 if (!aEndpoint.Bind(this)) {
215 // We can't recover from this.
216 MOZ_CRASH("Failed to bind RemoteDecoderManagerParent to endpoint");
217 }
218 AddRef();
219 }
220
ActorDealloc()221 void RemoteDecoderManagerParent::ActorDealloc() { Release(); }
222
RecvReadback(const SurfaceDescriptorGPUVideo & aSD,SurfaceDescriptor * aResult)223 mozilla::ipc::IPCResult RemoteDecoderManagerParent::RecvReadback(
224 const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult) {
225 const SurfaceDescriptorRemoteDecoder& sd = aSD;
226 RefPtr<Image> image = mImageMap[sd.handle()];
227 if (!image) {
228 *aResult = null_t();
229 return IPC_OK();
230 }
231
232 RefPtr<SourceSurface> source = image->GetAsSourceSurface();
233 if (!source) {
234 *aResult = null_t();
235 return IPC_OK();
236 }
237
238 SurfaceFormat format = source->GetFormat();
239 IntSize size = source->GetSize();
240 size_t length = ImageDataSerializer::ComputeRGBBufferSize(size, format);
241
242 Shmem buffer;
243 if (!length ||
244 !AllocShmem(length, Shmem::SharedMemory::TYPE_BASIC, &buffer)) {
245 *aResult = null_t();
246 return IPC_OK();
247 }
248
249 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
250 gfx::BackendType::CAIRO, buffer.get<uint8_t>(), size,
251 ImageDataSerializer::ComputeRGBStride(format, size.width), format);
252 if (!dt) {
253 DeallocShmem(buffer);
254 *aResult = null_t();
255 return IPC_OK();
256 }
257
258 dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
259 dt->Flush();
260
261 *aResult = SurfaceDescriptorBuffer(RGBDescriptor(size, format, true),
262 MemoryOrShmem(std::move(buffer)));
263 return IPC_OK();
264 }
265
266 mozilla::ipc::IPCResult
RecvDeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo & aSD)267 RemoteDecoderManagerParent::RecvDeallocateSurfaceDescriptorGPUVideo(
268 const SurfaceDescriptorGPUVideo& aSD) {
269 MOZ_ASSERT(OnManagerThread());
270 const SurfaceDescriptorRemoteDecoder& sd = aSD;
271 mImageMap.erase(sd.handle());
272 mTextureMap.erase(sd.handle());
273 return IPC_OK();
274 }
275
DeallocateSurfaceDescriptor(const SurfaceDescriptorGPUVideo & aSD)276 void RemoteDecoderManagerParent::DeallocateSurfaceDescriptor(
277 const SurfaceDescriptorGPUVideo& aSD) {
278 if (!OnManagerThread()) {
279 MOZ_ALWAYS_SUCCEEDS(
280 sRemoteDecoderManagerParentThread->Dispatch(NS_NewRunnableFunction(
281 "RemoteDecoderManagerParent::DeallocateSurfaceDescriptor",
282 [ref = RefPtr{this}, sd = aSD]() {
283 ref->RecvDeallocateSurfaceDescriptorGPUVideo(sd);
284 })));
285 } else {
286 RecvDeallocateSurfaceDescriptorGPUVideo(aSD);
287 }
288 }
289
290 } // namespace mozilla
291