Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=8 et ft=cpp : */
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 file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "CamerasChild.h"
8 :
9 : #undef FF
10 :
11 : #include "mozilla/Assertions.h"
12 : #include "mozilla/ipc/BackgroundChild.h"
13 : #include "mozilla/ipc/PBackgroundChild.h"
14 : #include "mozilla/Logging.h"
15 : #include "mozilla/SyncRunnable.h"
16 : #include "mozilla/WeakPtr.h"
17 : #include "mozilla/Unused.h"
18 : #include "MediaUtils.h"
19 : #include "nsThreadUtils.h"
20 :
21 : #undef LOG
22 : #undef LOG_ENABLED
23 : mozilla::LazyLogModule gCamerasChildLog("CamerasChild");
24 : #define LOG(args) MOZ_LOG(gCamerasChildLog, mozilla::LogLevel::Debug, args)
25 : #define LOG_ENABLED() MOZ_LOG_TEST(gCamerasChildLog, mozilla::LogLevel::Debug)
26 :
27 : #define FAKE_ONDEVICECHANGE_EVENT_PERIOD_IN_MS 5000
28 : #define FAKE_ONDEVICECHANGE_EVENT_REPEAT_COUNT 30
29 :
30 : namespace mozilla {
31 : namespace camera {
32 :
33 0 : CamerasSingleton::CamerasSingleton()
34 : : mCamerasMutex("CamerasSingleton::mCamerasMutex"),
35 : mCameras(nullptr),
36 : mCamerasChildThread(nullptr),
37 0 : mFakeDeviceChangeEventThread(nullptr) {
38 0 : LOG(("CamerasSingleton: %p", this));
39 0 : }
40 :
41 0 : CamerasSingleton::~CamerasSingleton() {
42 0 : LOG(("~CamerasSingleton: %p", this));
43 0 : }
44 :
45 0 : class FakeOnDeviceChangeEventRunnable : public Runnable
46 : {
47 : public:
48 0 : explicit FakeOnDeviceChangeEventRunnable(uint8_t counter)
49 0 : : Runnable("camera::FakeOnDeviceChangeEventRunnable")
50 0 : , mCounter(counter)
51 : {
52 0 : }
53 :
54 0 : NS_IMETHOD Run() override
55 : {
56 0 : OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
57 :
58 0 : CamerasChild* child = CamerasSingleton::Child();
59 0 : if (child) {
60 0 : child->OnDeviceChange();
61 :
62 0 : if (mCounter++ < FAKE_ONDEVICECHANGE_EVENT_REPEAT_COUNT) {
63 0 : RefPtr<FakeOnDeviceChangeEventRunnable> evt = new FakeOnDeviceChangeEventRunnable(mCounter);
64 0 : CamerasSingleton::FakeDeviceChangeEventThread()->DelayedDispatch(evt.forget(),
65 0 : FAKE_ONDEVICECHANGE_EVENT_PERIOD_IN_MS);
66 : }
67 : }
68 :
69 0 : return NS_OK;
70 : }
71 :
72 : private:
73 : uint8_t mCounter;
74 : };
75 :
76 0 : class InitializeIPCThread : public Runnable
77 : {
78 : public:
79 0 : InitializeIPCThread()
80 0 : : Runnable("camera::InitializeIPCThread")
81 0 : , mCamerasChild(nullptr)
82 : {
83 0 : }
84 :
85 0 : NS_IMETHOD Run() override {
86 : // Try to get the PBackground handle
87 : ipc::PBackgroundChild* existingBackgroundChild =
88 0 : ipc::BackgroundChild::GetForCurrentThread();
89 : // If it's not spun up yet, block until it is, and retry
90 0 : if (!existingBackgroundChild) {
91 0 : LOG(("No existingBackgroundChild"));
92 : existingBackgroundChild =
93 0 : ipc::BackgroundChild::SynchronouslyCreateForCurrentThread();
94 0 : LOG(("BackgroundChild: %p", existingBackgroundChild));
95 0 : if (!existingBackgroundChild) {
96 0 : return NS_ERROR_FAILURE;
97 : }
98 : }
99 :
100 : // Create CamerasChild
101 : // We will be returning the resulting pointer (synchronously) to our caller.
102 0 : mCamerasChild =
103 0 : static_cast<mozilla::camera::CamerasChild*>(existingBackgroundChild->SendPCamerasConstructor());
104 :
105 0 : return NS_OK;
106 : }
107 :
108 0 : CamerasChild* GetCamerasChild() {
109 0 : return mCamerasChild;
110 : }
111 :
112 : private:
113 : CamerasChild* mCamerasChild;
114 : };
115 :
116 : CamerasChild*
117 0 : GetCamerasChild() {
118 0 : CamerasSingleton::Mutex().AssertCurrentThreadOwns();
119 0 : if (!CamerasSingleton::Child()) {
120 0 : MOZ_ASSERT(!NS_IsMainThread(), "Should not be on the main Thread");
121 0 : MOZ_ASSERT(!CamerasSingleton::Thread());
122 0 : LOG(("No sCameras, setting up IPC Thread"));
123 0 : nsresult rv = NS_NewNamedThread("Cameras IPC",
124 0 : getter_AddRefs(CamerasSingleton::Thread()));
125 0 : if (NS_FAILED(rv)) {
126 0 : LOG(("Error launching IPC Thread"));
127 0 : return nullptr;
128 : }
129 :
130 : // At this point we are in the MediaManager thread, and the thread we are
131 : // dispatching to is the specific Cameras IPC thread that was just made
132 : // above, so now we will fire off a runnable to run
133 : // BackgroundChild::SynchronouslyCreateForCurrentThread there, while we
134 : // block in this thread.
135 : // We block until the following happens in the Cameras IPC thread:
136 : // 1) Creation of PBackground finishes
137 : // 2) Creation of PCameras finishes by sending a message to the parent
138 0 : RefPtr<InitializeIPCThread> runnable = new InitializeIPCThread();
139 0 : RefPtr<SyncRunnable> sr = new SyncRunnable(runnable);
140 0 : sr->DispatchToThread(CamerasSingleton::Thread());
141 0 : CamerasSingleton::Child() = runnable->GetCamerasChild();
142 : }
143 0 : if (!CamerasSingleton::Child()) {
144 0 : LOG(("Failed to set up CamerasChild, are we in shutdown?"));
145 : }
146 0 : return CamerasSingleton::Child();
147 : }
148 :
149 : CamerasChild*
150 0 : GetCamerasChildIfExists() {
151 0 : OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
152 0 : return CamerasSingleton::Child();
153 : }
154 :
155 0 : int CamerasChild::AddDeviceChangeCallback(DeviceChangeCallback* aCallback)
156 : {
157 : // According to the spec, if the script sets
158 : // navigator.mediaDevices.ondevicechange and the permission state is
159 : // "always granted", the User Agent MUST fires a devicechange event when
160 : // a new media input device is made available, even the script never
161 : // call getusermedia or enumerateDevices.
162 :
163 : // In order to detect the event, we need to init the camera engine.
164 : // Currently EnsureInitialized(aCapEngine) is only called when one of
165 : // CamerasaParent api, e.g., RecvNumberOfCaptureDevices(), is called.
166 :
167 : // So here we setup camera engine via EnsureInitialized(aCapEngine)
168 :
169 0 : EnsureInitialized(CameraEngine);
170 0 : return DeviceChangeCallback::AddDeviceChangeCallback(aCallback);
171 : }
172 :
173 : mozilla::ipc::IPCResult
174 0 : CamerasChild::RecvReplyFailure(void)
175 : {
176 0 : LOG((__PRETTY_FUNCTION__));
177 0 : MonitorAutoLock monitor(mReplyMonitor);
178 0 : mReceivedReply = true;
179 0 : mReplySuccess = false;
180 0 : monitor.Notify();
181 0 : return IPC_OK();
182 : }
183 :
184 : mozilla::ipc::IPCResult
185 0 : CamerasChild::RecvReplySuccess(void)
186 : {
187 0 : LOG((__PRETTY_FUNCTION__));
188 0 : MonitorAutoLock monitor(mReplyMonitor);
189 0 : mReceivedReply = true;
190 0 : mReplySuccess = true;
191 0 : monitor.Notify();
192 0 : return IPC_OK();
193 : }
194 :
195 : mozilla::ipc::IPCResult
196 0 : CamerasChild::RecvReplyNumberOfCapabilities(const int& numdev)
197 : {
198 0 : LOG((__PRETTY_FUNCTION__));
199 0 : MonitorAutoLock monitor(mReplyMonitor);
200 0 : mReceivedReply = true;
201 0 : mReplySuccess = true;
202 0 : mReplyInteger = numdev;
203 0 : monitor.Notify();
204 0 : return IPC_OK();
205 : }
206 :
207 : // Helper function to dispatch calls to the IPC Thread and
208 : // CamerasChild object. Takes the needed locks and dispatches.
209 : // Takes a "failed" value and a reference to the output variable
210 : // as parameters, will return the right one depending on whether
211 : // dispatching succeeded.
212 : template <class T = int>
213 0 : class LockAndDispatch
214 : {
215 : public:
216 0 : LockAndDispatch(CamerasChild* aCamerasChild,
217 : const char* aRequestingFunc,
218 : nsIRunnable *aRunnable,
219 : const T& aFailureValue = T(-1), const T& aSuccessValue = T(0))
220 : : mCamerasChild(aCamerasChild), mRequestingFunc(aRequestingFunc),
221 : mRunnable(aRunnable),
222 : mReplyLock(aCamerasChild->mReplyMonitor),
223 : mRequestLock(aCamerasChild->mRequestMutex),
224 : mSuccess(true),
225 0 : mFailureValue(aFailureValue), mSuccessValue(aSuccessValue)
226 : {
227 0 : Dispatch();
228 0 : }
229 :
230 0 : T ReturnValue() const {
231 0 : if (mSuccess) {
232 0 : return mSuccessValue;
233 : } else {
234 0 : return mFailureValue;
235 : }
236 : }
237 :
238 0 : const bool& Success() const {
239 0 : return mSuccess;
240 : }
241 :
242 : private:
243 0 : void Dispatch() {
244 0 : if (!mCamerasChild->DispatchToParent(mRunnable, mReplyLock)) {
245 0 : LOG(("Cameras dispatch for IPC failed in %s", mRequestingFunc));
246 0 : mSuccess = false;
247 : }
248 0 : }
249 :
250 : CamerasChild* mCamerasChild;
251 : const char* mRequestingFunc;
252 : nsIRunnable* mRunnable;
253 : // Prevent concurrent use of the reply variables by holding
254 : // the mReplyMonitor. Note that this is unlocked while waiting for
255 : // the reply to be filled in, necessitating the additional mRequestLock/Mutex;
256 : MonitorAutoLock mReplyLock;
257 : MutexAutoLock mRequestLock;
258 : bool mSuccess;
259 : const T& mFailureValue;
260 : const T& mSuccessValue;
261 : };
262 :
263 : bool
264 0 : CamerasChild::DispatchToParent(nsIRunnable* aRunnable,
265 : MonitorAutoLock& aMonitor)
266 : {
267 0 : CamerasSingleton::Mutex().AssertCurrentThreadOwns();
268 0 : CamerasSingleton::Thread()->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
269 : // We can't see if the send worked, so we need to be able to bail
270 : // out on shutdown (when it failed and we won't get a reply).
271 0 : if (!mIPCIsAlive) {
272 0 : return false;
273 : }
274 : // Guard against spurious wakeups.
275 0 : mReceivedReply = false;
276 : // Wait for a reply
277 0 : do {
278 0 : aMonitor.Wait();
279 0 : } while (!mReceivedReply && mIPCIsAlive);
280 0 : if (!mReplySuccess) {
281 0 : return false;
282 : }
283 0 : return true;
284 : }
285 :
286 : int
287 0 : CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine,
288 : const char* deviceUniqueIdUTF8)
289 : {
290 0 : LOG((__PRETTY_FUNCTION__));
291 0 : LOG(("NumberOfCapabilities for %s", deviceUniqueIdUTF8));
292 0 : nsCString unique_id(deviceUniqueIdUTF8);
293 : nsCOMPtr<nsIRunnable> runnable =
294 0 : mozilla::NewNonOwningRunnableMethod<CaptureEngine, nsCString>(
295 : "camera::PCamerasChild::SendNumberOfCapabilities",
296 : this,
297 : &CamerasChild::SendNumberOfCapabilities,
298 : aCapEngine,
299 0 : unique_id);
300 0 : LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger);
301 0 : LOG(("Capture capability count: %d", dispatcher.ReturnValue()));
302 0 : return dispatcher.ReturnValue();
303 : }
304 :
305 : int
306 0 : CamerasChild::NumberOfCaptureDevices(CaptureEngine aCapEngine)
307 : {
308 0 : LOG((__PRETTY_FUNCTION__));
309 : nsCOMPtr<nsIRunnable> runnable =
310 0 : mozilla::NewNonOwningRunnableMethod<CaptureEngine>(
311 : "camera::PCamerasChild::SendNumberOfCaptureDevices",
312 : this,
313 : &CamerasChild::SendNumberOfCaptureDevices,
314 0 : aCapEngine);
315 0 : LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger);
316 0 : LOG(("Capture Devices: %d", dispatcher.ReturnValue()));
317 0 : return dispatcher.ReturnValue();
318 : }
319 :
320 : mozilla::ipc::IPCResult
321 0 : CamerasChild::RecvReplyNumberOfCaptureDevices(const int& numdev)
322 : {
323 0 : LOG((__PRETTY_FUNCTION__));
324 0 : MonitorAutoLock monitor(mReplyMonitor);
325 0 : mReceivedReply = true;
326 0 : mReplySuccess = true;
327 0 : mReplyInteger = numdev;
328 0 : monitor.Notify();
329 0 : return IPC_OK();
330 : }
331 :
332 : int
333 0 : CamerasChild::EnsureInitialized(CaptureEngine aCapEngine)
334 : {
335 0 : LOG((__PRETTY_FUNCTION__));
336 : nsCOMPtr<nsIRunnable> runnable =
337 0 : mozilla::NewNonOwningRunnableMethod<CaptureEngine>(
338 : "camera::PCamerasChild::SendEnsureInitialized",
339 : this,
340 : &CamerasChild::SendEnsureInitialized,
341 0 : aCapEngine);
342 0 : LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger);
343 0 : LOG(("Capture Devices: %d", dispatcher.ReturnValue()));
344 0 : return dispatcher.ReturnValue();
345 : }
346 :
347 : int
348 0 : CamerasChild::GetCaptureCapability(CaptureEngine aCapEngine,
349 : const char* unique_idUTF8,
350 : const unsigned int capability_number,
351 : webrtc::VideoCaptureCapability& capability)
352 : {
353 0 : LOG(("GetCaptureCapability: %s %d", unique_idUTF8, capability_number));
354 0 : nsCString unique_id(unique_idUTF8);
355 : nsCOMPtr<nsIRunnable> runnable =
356 0 : mozilla::NewNonOwningRunnableMethod<CaptureEngine, nsCString, unsigned int>(
357 : "camera::PCamerasChild::SendGetCaptureCapability",
358 : this,
359 : &CamerasChild::SendGetCaptureCapability,
360 : aCapEngine,
361 : unique_id,
362 0 : capability_number);
363 0 : LockAndDispatch<> dispatcher(this, __func__, runnable);
364 0 : if (dispatcher.Success()) {
365 0 : capability = mReplyCapability;
366 : }
367 0 : return dispatcher.ReturnValue();
368 : }
369 :
370 : mozilla::ipc::IPCResult
371 0 : CamerasChild::RecvReplyGetCaptureCapability(const VideoCaptureCapability& ipcCapability)
372 : {
373 0 : LOG((__PRETTY_FUNCTION__));
374 0 : MonitorAutoLock monitor(mReplyMonitor);
375 0 : mReceivedReply = true;
376 0 : mReplySuccess = true;
377 0 : mReplyCapability.width = ipcCapability.width();
378 0 : mReplyCapability.height = ipcCapability.height();
379 0 : mReplyCapability.maxFPS = ipcCapability.maxFPS();
380 0 : mReplyCapability.expectedCaptureDelay = ipcCapability.expectedCaptureDelay();
381 0 : mReplyCapability.rawType = static_cast<webrtc::RawVideoType>(ipcCapability.rawType());
382 0 : mReplyCapability.codecType = static_cast<webrtc::VideoCodecType>(ipcCapability.codecType());
383 0 : mReplyCapability.interlaced = ipcCapability.interlaced();
384 0 : monitor.Notify();
385 0 : return IPC_OK();
386 : }
387 :
388 : int
389 0 : CamerasChild::GetCaptureDevice(CaptureEngine aCapEngine,
390 : unsigned int list_number, char* device_nameUTF8,
391 : const unsigned int device_nameUTF8Length,
392 : char* unique_idUTF8,
393 : const unsigned int unique_idUTF8Length,
394 : bool* scary)
395 : {
396 0 : LOG((__PRETTY_FUNCTION__));
397 : nsCOMPtr<nsIRunnable> runnable =
398 0 : mozilla::NewNonOwningRunnableMethod<CaptureEngine, unsigned int>(
399 : "camera::PCamerasChild::SendGetCaptureDevice",
400 : this,
401 : &CamerasChild::SendGetCaptureDevice,
402 : aCapEngine,
403 0 : list_number);
404 0 : LockAndDispatch<> dispatcher(this, __func__, runnable);
405 0 : if (dispatcher.Success()) {
406 0 : base::strlcpy(device_nameUTF8, mReplyDeviceName.get(), device_nameUTF8Length);
407 0 : base::strlcpy(unique_idUTF8, mReplyDeviceID.get(), unique_idUTF8Length);
408 0 : if (scary) {
409 0 : *scary = mReplyScary;
410 : }
411 0 : LOG(("Got %s name %s id", device_nameUTF8, unique_idUTF8));
412 : }
413 0 : return dispatcher.ReturnValue();
414 : }
415 :
416 : mozilla::ipc::IPCResult
417 0 : CamerasChild::RecvReplyGetCaptureDevice(const nsCString& device_name,
418 : const nsCString& device_id,
419 : const bool& scary)
420 : {
421 0 : LOG((__PRETTY_FUNCTION__));
422 0 : MonitorAutoLock monitor(mReplyMonitor);
423 0 : mReceivedReply = true;
424 0 : mReplySuccess = true;
425 0 : mReplyDeviceName = device_name;
426 0 : mReplyDeviceID = device_id;
427 0 : mReplyScary = scary;
428 0 : monitor.Notify();
429 0 : return IPC_OK();
430 : }
431 :
432 : int
433 0 : CamerasChild::AllocateCaptureDevice(CaptureEngine aCapEngine,
434 : const char* unique_idUTF8,
435 : const unsigned int unique_idUTF8Length,
436 : int& aStreamId,
437 : const mozilla::ipc::PrincipalInfo& aPrincipalInfo)
438 : {
439 0 : LOG((__PRETTY_FUNCTION__));
440 0 : nsCString unique_id(unique_idUTF8);
441 : nsCOMPtr<nsIRunnable> runnable =
442 : mozilla::NewNonOwningRunnableMethod<CaptureEngine,
443 : nsCString,
444 0 : const mozilla::ipc::PrincipalInfo&>(
445 : "camera::PCamerasChild::SendAllocateCaptureDevice",
446 : this,
447 : &CamerasChild::SendAllocateCaptureDevice,
448 : aCapEngine,
449 : unique_id,
450 0 : aPrincipalInfo);
451 0 : LockAndDispatch<> dispatcher(this, __func__, runnable);
452 0 : if (dispatcher.Success()) {
453 0 : LOG(("Capture Device allocated: %d", mReplyInteger));
454 0 : aStreamId = mReplyInteger;
455 : }
456 0 : return dispatcher.ReturnValue();
457 : }
458 :
459 :
460 : mozilla::ipc::IPCResult
461 0 : CamerasChild::RecvReplyAllocateCaptureDevice(const int& numdev)
462 : {
463 0 : LOG((__PRETTY_FUNCTION__));
464 0 : MonitorAutoLock monitor(mReplyMonitor);
465 0 : mReceivedReply = true;
466 0 : mReplySuccess = true;
467 0 : mReplyInteger = numdev;
468 0 : monitor.Notify();
469 0 : return IPC_OK();
470 : }
471 :
472 : int
473 0 : CamerasChild::ReleaseCaptureDevice(CaptureEngine aCapEngine,
474 : const int capture_id)
475 : {
476 0 : LOG((__PRETTY_FUNCTION__));
477 : nsCOMPtr<nsIRunnable> runnable =
478 0 : mozilla::NewNonOwningRunnableMethod<CaptureEngine, int>(
479 : "camera::PCamerasChild::SendReleaseCaptureDevice",
480 : this,
481 : &CamerasChild::SendReleaseCaptureDevice,
482 : aCapEngine,
483 0 : capture_id);
484 0 : LockAndDispatch<> dispatcher(this, __func__, runnable);
485 0 : return dispatcher.ReturnValue();
486 : }
487 :
488 : void
489 0 : CamerasChild::AddCallback(const CaptureEngine aCapEngine, const int capture_id,
490 : FrameRelay* render)
491 : {
492 0 : MutexAutoLock lock(mCallbackMutex);
493 : CapturerElement ce;
494 0 : ce.engine = aCapEngine;
495 0 : ce.id = capture_id;
496 0 : ce.callback = render;
497 0 : mCallbacks.AppendElement(ce);
498 0 : }
499 :
500 : void
501 0 : CamerasChild::RemoveCallback(const CaptureEngine aCapEngine, const int capture_id)
502 : {
503 0 : MutexAutoLock lock(mCallbackMutex);
504 0 : for (unsigned int i = 0; i < mCallbacks.Length(); i++) {
505 0 : CapturerElement ce = mCallbacks[i];
506 0 : if (ce.engine == aCapEngine && ce.id == capture_id) {
507 0 : mCallbacks.RemoveElementAt(i);
508 0 : break;
509 : }
510 : }
511 0 : }
512 :
513 : int
514 0 : CamerasChild::StartCapture(CaptureEngine aCapEngine,
515 : const int capture_id,
516 : webrtc::VideoCaptureCapability& webrtcCaps,
517 : FrameRelay* cb)
518 : {
519 0 : LOG((__PRETTY_FUNCTION__));
520 0 : AddCallback(aCapEngine, capture_id, cb);
521 : VideoCaptureCapability capCap(webrtcCaps.width,
522 : webrtcCaps.height,
523 : webrtcCaps.maxFPS,
524 : webrtcCaps.expectedCaptureDelay,
525 0 : webrtcCaps.rawType,
526 0 : webrtcCaps.codecType,
527 0 : webrtcCaps.interlaced);
528 : nsCOMPtr<nsIRunnable> runnable = mozilla::
529 0 : NewNonOwningRunnableMethod<CaptureEngine, int, VideoCaptureCapability>(
530 : "camera::PCamerasChild::SendStartCapture",
531 : this,
532 : &CamerasChild::SendStartCapture,
533 : aCapEngine,
534 : capture_id,
535 0 : capCap);
536 0 : LockAndDispatch<> dispatcher(this, __func__, runnable);
537 0 : return dispatcher.ReturnValue();
538 : }
539 :
540 : int
541 0 : CamerasChild::StopCapture(CaptureEngine aCapEngine, const int capture_id)
542 : {
543 0 : LOG((__PRETTY_FUNCTION__));
544 : nsCOMPtr<nsIRunnable> runnable =
545 0 : mozilla::NewNonOwningRunnableMethod<CaptureEngine, int>(
546 : "camera::PCamerasChild::SendStopCapture",
547 : this,
548 : &CamerasChild::SendStopCapture,
549 : aCapEngine,
550 0 : capture_id);
551 0 : LockAndDispatch<> dispatcher(this, __func__, runnable);
552 0 : if (dispatcher.Success()) {
553 0 : RemoveCallback(aCapEngine, capture_id);
554 : }
555 0 : return dispatcher.ReturnValue();
556 : }
557 :
558 : void
559 0 : Shutdown(void)
560 : {
561 0 : OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
562 0 : CamerasChild* child = CamerasSingleton::Child();
563 0 : if (!child) {
564 : // We don't want to cause everything to get fired up if we're
565 : // really already shut down.
566 0 : LOG(("Shutdown when already shut down"));
567 0 : return;
568 : }
569 0 : child->ShutdownAll();
570 : }
571 :
572 0 : class ShutdownRunnable : public Runnable {
573 : public:
574 0 : explicit ShutdownRunnable(already_AddRefed<Runnable>&& aReplyEvent)
575 0 : : Runnable("camera::ShutdownRunnable")
576 0 : , mReplyEvent(aReplyEvent){};
577 :
578 0 : NS_IMETHOD Run() override {
579 0 : LOG(("Closing BackgroundChild"));
580 0 : ipc::BackgroundChild::CloseForCurrentThread();
581 :
582 0 : NS_DispatchToMainThread(mReplyEvent.forget());
583 :
584 0 : return NS_OK;
585 : }
586 :
587 : private:
588 : RefPtr<Runnable> mReplyEvent;
589 : };
590 :
591 : void
592 0 : CamerasChild::ShutdownAll()
593 : {
594 : // Called with CamerasSingleton::Mutex() held
595 0 : ShutdownParent();
596 0 : ShutdownChild();
597 0 : }
598 :
599 : void
600 0 : CamerasChild::ShutdownParent()
601 : {
602 : // Called with CamerasSingleton::Mutex() held
603 : {
604 0 : MonitorAutoLock monitor(mReplyMonitor);
605 0 : mIPCIsAlive = false;
606 0 : monitor.NotifyAll();
607 : }
608 0 : if (CamerasSingleton::Thread()) {
609 0 : LOG(("Dispatching actor deletion"));
610 : // Delete the parent actor.
611 : // CamerasChild (this) will remain alive and is only deleted by the
612 : // IPC layer when SendAllDone returns.
613 0 : nsCOMPtr<nsIRunnable> deleteRunnable = mozilla::NewNonOwningRunnableMethod(
614 0 : "camera::PCamerasChild::SendAllDone", this, &CamerasChild::SendAllDone);
615 0 : CamerasSingleton::Thread()->Dispatch(deleteRunnable, NS_DISPATCH_NORMAL);
616 : } else {
617 0 : LOG(("ShutdownParent called without PBackground thread"));
618 : }
619 0 : }
620 :
621 : void
622 0 : CamerasChild::ShutdownChild()
623 : {
624 : // Called with CamerasSingleton::Mutex() held
625 0 : if (CamerasSingleton::Thread()) {
626 0 : LOG(("PBackground thread exists, dispatching close"));
627 : // Dispatch closing the IPC thread back to us when the
628 : // BackgroundChild is closed.
629 0 : RefPtr<ShutdownRunnable> runnable = new ShutdownRunnable(NewRunnableMethod(
630 0 : "nsIThread::Shutdown", CamerasSingleton::Thread(), &nsIThread::Shutdown));
631 0 : CamerasSingleton::Thread()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
632 : } else {
633 0 : LOG(("Shutdown called without PBackground thread"));
634 : }
635 0 : LOG(("Erasing sCameras & thread refs (original thread)"));
636 0 : CamerasSingleton::Child() = nullptr;
637 0 : CamerasSingleton::Thread() = nullptr;
638 :
639 0 : if (CamerasSingleton::FakeDeviceChangeEventThread()) {
640 : RefPtr<ShutdownRunnable> runnable = new ShutdownRunnable(
641 0 : NewRunnableMethod("nsIThread::Shutdown",
642 : CamerasSingleton::FakeDeviceChangeEventThread(),
643 0 : &nsIThread::Shutdown));
644 0 : CamerasSingleton::FakeDeviceChangeEventThread()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
645 : }
646 0 : CamerasSingleton::FakeDeviceChangeEventThread() = nullptr;
647 0 : }
648 :
649 : mozilla::ipc::IPCResult
650 0 : CamerasChild::RecvDeliverFrame(const CaptureEngine& capEngine,
651 : const int& capId,
652 : mozilla::ipc::Shmem&& shmem,
653 : const VideoFrameProperties & prop)
654 : {
655 0 : MutexAutoLock lock(mCallbackMutex);
656 0 : if (Callback(capEngine, capId)) {
657 0 : unsigned char* image = shmem.get<unsigned char>();
658 0 : Callback(capEngine, capId)->DeliverFrame(image, prop);
659 : } else {
660 0 : LOG(("DeliverFrame called with dead callback"));
661 : }
662 0 : SendReleaseFrame(shmem);
663 0 : return IPC_OK();
664 : }
665 :
666 : mozilla::ipc::IPCResult
667 0 : CamerasChild::RecvDeviceChange()
668 : {
669 0 : this->OnDeviceChange();
670 0 : return IPC_OK();
671 : }
672 :
673 : int
674 0 : CamerasChild::SetFakeDeviceChangeEvents()
675 : {
676 0 : CamerasSingleton::Mutex().AssertCurrentThreadOwns();
677 :
678 0 : if(!CamerasSingleton::FakeDeviceChangeEventThread()) {
679 0 : nsresult rv = NS_NewNamedThread("Fake DC Event",
680 0 : getter_AddRefs(CamerasSingleton::FakeDeviceChangeEventThread()));
681 0 : if (NS_FAILED(rv)) {
682 0 : LOG(("Error launching Fake OnDeviceChange Event Thread"));
683 0 : return -1;
684 : }
685 : }
686 :
687 : // To simulate the devicechange event in mochitest,
688 : // we fire a fake devicechange event in Camera IPC thread periodically
689 0 : RefPtr<FakeOnDeviceChangeEventRunnable> evt = new FakeOnDeviceChangeEventRunnable(0);
690 0 : CamerasSingleton::FakeDeviceChangeEventThread()->Dispatch(evt.forget(), NS_DISPATCH_NORMAL);
691 :
692 0 : return 0;
693 : }
694 :
695 : mozilla::ipc::IPCResult
696 0 : CamerasChild::RecvFrameSizeChange(const CaptureEngine& capEngine,
697 : const int& capId,
698 : const int& w, const int& h)
699 : {
700 0 : LOG((__PRETTY_FUNCTION__));
701 0 : MutexAutoLock lock(mCallbackMutex);
702 0 : if (Callback(capEngine, capId)) {
703 0 : Callback(capEngine, capId)->FrameSizeChange(w, h);
704 : } else {
705 0 : LOG(("Frame size change with dead callback"));
706 : }
707 0 : return IPC_OK();
708 : }
709 :
710 : void
711 0 : CamerasChild::ActorDestroy(ActorDestroyReason aWhy)
712 : {
713 0 : MonitorAutoLock monitor(mReplyMonitor);
714 0 : mIPCIsAlive = false;
715 : // Hopefully prevent us from getting stuck
716 : // on replies that'll never come.
717 0 : monitor.NotifyAll();
718 0 : }
719 :
720 0 : CamerasChild::CamerasChild()
721 : : mCallbackMutex("mozilla::cameras::CamerasChild::mCallbackMutex"),
722 : mIPCIsAlive(true),
723 : mRequestMutex("mozilla::cameras::CamerasChild::mRequestMutex"),
724 0 : mReplyMonitor("mozilla::cameras::CamerasChild::mReplyMonitor")
725 : {
726 0 : LOG(("CamerasChild: %p", this));
727 :
728 0 : MOZ_COUNT_CTOR(CamerasChild);
729 0 : }
730 :
731 0 : CamerasChild::~CamerasChild()
732 : {
733 0 : LOG(("~CamerasChild: %p", this));
734 :
735 : {
736 0 : OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
737 : // In normal circumstances we've already shut down and the
738 : // following does nothing. But on fatal IPC errors we will
739 : // get destructed immediately, and should not try to reach
740 : // the parent.
741 0 : ShutdownChild();
742 : }
743 :
744 0 : MOZ_COUNT_DTOR(CamerasChild);
745 0 : }
746 :
747 0 : FrameRelay* CamerasChild::Callback(CaptureEngine aCapEngine,
748 : int capture_id)
749 : {
750 0 : for (unsigned int i = 0; i < mCallbacks.Length(); i++) {
751 0 : CapturerElement ce = mCallbacks[i];
752 0 : if (ce.engine == aCapEngine && ce.id == capture_id) {
753 0 : return ce.callback;
754 : }
755 : }
756 :
757 0 : return nullptr;
758 : }
759 :
760 : }
761 : }
|