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