Coverage Report

Created: 2018-09-25 14:53

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