Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/systemservices/CamerasParent.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 "CamerasParent.h"
8
#include "MediaEngineSource.h"
9
#include "MediaUtils.h"
10
#include "VideoFrameUtils.h"
11
12
#include "mozilla/Assertions.h"
13
#include "mozilla/Unused.h"
14
#include "mozilla/Services.h"
15
#include "mozilla/Logging.h"
16
#include "mozilla/ipc/BackgroundParent.h"
17
#include "mozilla/ipc/PBackgroundParent.h"
18
#include "mozilla/Preferences.h"
19
#include "nsIPermissionManager.h"
20
#include "nsThreadUtils.h"
21
#include "nsXPCOM.h"
22
#include "nsNetUtil.h"
23
24
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
25
26
#if defined(_WIN32)
27
#include <process.h>
28
#define getpid() _getpid()
29
#endif
30
31
#undef LOG
32
#undef LOG_VERBOSE
33
#undef LOG_ENABLED
34
mozilla::LazyLogModule gCamerasParentLog("CamerasParent");
35
0
#define LOG(args) MOZ_LOG(gCamerasParentLog, mozilla::LogLevel::Debug, args)
36
0
#define LOG_VERBOSE(args) MOZ_LOG(gCamerasParentLog, mozilla::LogLevel::Verbose, args)
37
#define LOG_ENABLED() MOZ_LOG_TEST(gCamerasParentLog, mozilla::LogLevel::Debug)
38
39
namespace mozilla {
40
namespace camera {
41
42
std::map<uint32_t, const char *> sDeviceUniqueIDs;
43
std::map<uint32_t, webrtc::VideoCaptureCapability> sAllRequestedCapabilities;
44
45
uint32_t
46
ResolutionFeasibilityDistance(int32_t candidate, int32_t requested)
47
0
{
48
0
  // The purpose of this function is to find a smallest resolution
49
0
  // which is larger than all requested capabilities.
50
0
  // Then we can use down-scaling to fulfill each request.
51
0
52
0
  MOZ_DIAGNOSTIC_ASSERT(candidate >= 0, "Candidate unexpectedly negative");
53
0
  MOZ_DIAGNOSTIC_ASSERT(requested >= 0, "Requested unexpectedly negative");
54
0
55
0
  if (candidate == 0) {
56
0
    // Treat width|height capability of 0 as "can do any".
57
0
    // This allows for orthogonal capabilities that are not in discrete steps.
58
0
    return 0;
59
0
  }
60
0
61
0
  uint32_t distance =
62
0
    std::abs(candidate - requested) * 1000 / std::max(candidate, requested);
63
0
  if (candidate >= requested) {
64
0
    // This is a good case, the candidate covers the requested resolution.
65
0
    return distance;
66
0
  }
67
0
68
0
  // This is a bad case, the candidate is lower than the requested resolution.
69
0
  // This is penalized with an added weight of 10000.
70
0
  return 10000 + distance;
71
0
}
72
73
uint32_t
74
FeasibilityDistance(int32_t candidate, int32_t requested)
75
0
{
76
0
  MOZ_DIAGNOSTIC_ASSERT(candidate >= 0, "Candidate unexpectedly negative");
77
0
  MOZ_DIAGNOSTIC_ASSERT(requested >= 0, "Requested unexpectedly negative");
78
0
79
0
  if (candidate == 0) {
80
0
    // Treat maxFPS capability of 0 as "can do any".
81
0
    // This allows for orthogonal capabilities that are not in discrete steps.
82
0
    return 0;
83
0
  }
84
0
85
0
  return std::abs(candidate - requested) * 1000 / std::max(candidate, requested);
86
0
}
87
88
StaticRefPtr<VideoEngine> CamerasParent::sEngines[CaptureEngine::MaxEngine];
89
int32_t CamerasParent::sNumOfOpenCamerasParentEngines = 0;
90
int32_t CamerasParent::sNumOfCamerasParents = 0;
91
base::Thread* CamerasParent::sVideoCaptureThread = nullptr;
92
Monitor* CamerasParent::sThreadMonitor = nullptr;
93
StaticMutex CamerasParent::sMutex;
94
95
// 3 threads are involved in this code:
96
// - the main thread for some setups, and occassionally for video capture setup
97
//   calls that don't work correctly elsewhere.
98
// - the IPC thread on which PBackground is running and which receives and
99
//   sends messages
100
// - a thread which will execute the actual (possibly slow) camera access
101
//   called "VideoCapture". On Windows this is a thread with an event loop
102
//   suitable for UI access.
103
104
// InputObserver is owned by CamerasParent, and it has a ref to CamerasParent
105
0
void InputObserver::OnDeviceChange() {
106
0
  LOG((__PRETTY_FUNCTION__));
107
0
  MOZ_ASSERT(mParent);
108
0
109
0
  RefPtr<InputObserver> self(this);
110
0
  RefPtr<nsIRunnable> ipc_runnable =
111
0
    media::NewRunnableFrom([self]() -> nsresult {
112
0
      if (self->mParent->IsShuttingDown()) {
113
0
        return NS_ERROR_FAILURE;
114
0
      }
115
0
      Unused << self->mParent->SendDeviceChange();
116
0
      return NS_OK;
117
0
    });
118
0
119
0
  nsIEventTarget* target = mParent->GetBackgroundEventTarget();
120
0
  MOZ_ASSERT(target != nullptr);
121
0
  target->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
122
0
};
123
124
class DeliverFrameRunnable : public mozilla::Runnable {
125
public:
126
  DeliverFrameRunnable(CamerasParent* aParent,
127
                       CaptureEngine aEngine,
128
                       uint32_t aStreamId,
129
                       const webrtc::VideoFrame& aFrame,
130
                       const VideoFrameProperties& aProperties)
131
    : Runnable("camera::DeliverFrameRunnable")
132
    , mParent(aParent)
133
    , mCapEngine(aEngine)
134
    , mStreamId(aStreamId)
135
    , mProperties(aProperties)
136
    , mResult(0)
137
0
  {
138
0
    // No ShmemBuffer (of the right size) was available, so make an
139
0
    // extra buffer here.  We have no idea when we are going to run and
140
0
    // it will be potentially long after the webrtc frame callback has
141
0
    // returned, so the copy needs to be no later than here.
142
0
    // We will need to copy this back into a Shmem later on so we prefer
143
0
    // using ShmemBuffers to avoid the extra copy.
144
0
    mAlternateBuffer.reset(new unsigned char[aProperties.bufferSize()]);
145
0
    VideoFrameUtils::CopyVideoFrameBuffers(mAlternateBuffer.get(),
146
0
                                           aProperties.bufferSize(), aFrame);
147
0
  }
148
149
  DeliverFrameRunnable(CamerasParent* aParent,
150
                       CaptureEngine aEngine,
151
                       uint32_t aStreamId,
152
                       ShmemBuffer aBuffer,
153
                       VideoFrameProperties& aProperties)
154
    : Runnable("camera::DeliverFrameRunnable")
155
    , mParent(aParent)
156
    , mCapEngine(aEngine)
157
    , mStreamId(aStreamId)
158
    , mBuffer(std::move(aBuffer))
159
    , mProperties(aProperties)
160
0
    , mResult(0) {};
161
162
0
  NS_IMETHOD Run() override {
163
0
    if (mParent->IsShuttingDown()) {
164
0
      // Communication channel is being torn down
165
0
      mResult = 0;
166
0
      return NS_OK;
167
0
    }
168
0
    if (!mParent->DeliverFrameOverIPC(mCapEngine, mStreamId, std::move(mBuffer),
169
0
                                      mAlternateBuffer.get(), mProperties)) {
170
0
      mResult = -1;
171
0
    } else {
172
0
      mResult = 0;
173
0
    }
174
0
    return NS_OK;
175
0
  }
176
177
0
  int GetResult() {
178
0
    return mResult;
179
0
  }
180
181
private:
182
  RefPtr<CamerasParent> mParent;
183
  CaptureEngine mCapEngine;
184
  uint32_t mStreamId;
185
  ShmemBuffer mBuffer;
186
  UniquePtr<unsigned char[]> mAlternateBuffer;
187
  VideoFrameProperties mProperties;
188
  int mResult;
189
};
190
191
NS_IMPL_ISUPPORTS(CamerasParent, nsIObserver)
192
193
NS_IMETHODIMP
194
CamerasParent::Observe(nsISupports *aSubject,
195
                       const char *aTopic,
196
                       const char16_t *aData)
197
0
{
198
0
  MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID));
199
0
  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
200
0
  MOZ_ASSERT(obs);
201
0
  obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
202
0
  StopVideoCapture();
203
0
  return NS_OK;
204
0
}
205
206
nsresult
207
CamerasParent::DispatchToVideoCaptureThread(Runnable* event)
208
0
{
209
0
  // Don't try to dispatch if we're already on the right thread.
210
0
  // There's a potential deadlock because the sThreadMonitor is likely
211
0
  // to be taken already.
212
0
  MonitorAutoLock lock(*sThreadMonitor);
213
0
  MOZ_ASSERT(!sVideoCaptureThread ||
214
0
             sVideoCaptureThread->thread_id() != PlatformThread::CurrentId());
215
0
216
0
  while(mChildIsAlive && mWebRTCAlive &&
217
0
        (!sVideoCaptureThread || !sVideoCaptureThread->IsRunning())) {
218
0
    sThreadMonitor->Wait();
219
0
  }
220
0
  if (!sVideoCaptureThread || !sVideoCaptureThread->IsRunning()) {
221
0
    return NS_ERROR_FAILURE;
222
0
  }
223
0
  RefPtr<Runnable> addrefedEvent = event;
224
0
  sVideoCaptureThread->message_loop()->PostTask(addrefedEvent.forget());
225
0
  return NS_OK;
226
0
}
227
228
void
229
CamerasParent::StopVideoCapture()
230
0
{
231
0
  LOG((__PRETTY_FUNCTION__));
232
0
  // We are called from the main thread (xpcom-shutdown) or
233
0
  // from PBackground (when the Actor shuts down).
234
0
  // Shut down the WebRTC stack (on the capture thread)
235
0
  RefPtr<CamerasParent> self(this);
236
0
  RefPtr<Runnable> webrtc_runnable =
237
0
    media::NewRunnableFrom([self]() -> nsresult {
238
0
      MonitorAutoLock lock(*(self->sThreadMonitor));
239
0
      self->CloseEngines();
240
0
      self->sThreadMonitor->NotifyAll();
241
0
      return NS_OK;
242
0
    });
243
0
  DebugOnly<nsresult> rv = DispatchToVideoCaptureThread(webrtc_runnable);
244
#ifdef DEBUG
245
  // It's ok for the dispatch to fail if the cleanup it has to do
246
  // has been done already.
247
  MOZ_ASSERT(NS_SUCCEEDED(rv) || !mWebRTCAlive);
248
#endif
249
  // Hold here until the WebRTC thread is gone. We need to dispatch
250
0
  // the thread deletion *now*, or there will be no more possibility
251
0
  // to get to the main thread.
252
0
  MonitorAutoLock lock(*sThreadMonitor);
253
0
  while (mWebRTCAlive) {
254
0
    sThreadMonitor->Wait();
255
0
  }
256
0
  // After closing the WebRTC stack, clean up the
257
0
  // VideoCapture thread.
258
0
  if (sNumOfOpenCamerasParentEngines == 0 && self->sVideoCaptureThread) {
259
0
    base::Thread *thread = self->sVideoCaptureThread;
260
0
    self->sVideoCaptureThread = nullptr;
261
0
    RefPtr<Runnable> threadShutdown =
262
0
      media::NewRunnableFrom([thread]() -> nsresult {
263
0
        if (thread->IsRunning()) {
264
0
          thread->Stop();
265
0
        }
266
0
        delete thread;
267
0
        return NS_OK;
268
0
      });
269
0
    if (NS_FAILED(NS_DispatchToMainThread(threadShutdown))) {
270
0
      LOG(("Could not dispatch VideoCaptureThread destruction"));
271
0
    }
272
0
  }
273
0
}
274
275
int
276
CamerasParent::DeliverFrameOverIPC(CaptureEngine capEng,
277
                          uint32_t aStreamId,
278
                          ShmemBuffer buffer,
279
                          unsigned char* altbuffer,
280
                          VideoFrameProperties& aProps)
281
0
{
282
0
  // No ShmemBuffers were available, so construct one now of the right size
283
0
  // and copy into it. That is an extra copy, but we expect this to be
284
0
  // the exceptional case, because we just assured the next call *will* have a
285
0
  // buffer of the right size.
286
0
  if (altbuffer != nullptr) {
287
0
    // Get a shared memory buffer from the pool, at least size big
288
0
    ShmemBuffer shMemBuff = mShmemPool.Get(this, aProps.bufferSize());
289
0
290
0
    if (!shMemBuff.Valid()) {
291
0
      LOG(("No usable Video shmem in DeliverFrame (out of buffers?)"));
292
0
      // We can skip this frame if we run out of buffers, it's not a real error.
293
0
      return 0;
294
0
    }
295
0
296
0
    // get() and Size() check for proper alignment of the segment
297
0
    memcpy(shMemBuff.GetBytes(), altbuffer, aProps.bufferSize());
298
0
299
0
    if (!SendDeliverFrame(capEng, aStreamId,
300
0
                          shMemBuff.Get(), aProps)) {
301
0
      return -1;
302
0
    }
303
0
  } else {
304
0
    MOZ_ASSERT(buffer.Valid());
305
0
    // ShmemBuffer was available, we're all good. A single copy happened
306
0
    // in the original webrtc callback.
307
0
    if (!SendDeliverFrame(capEng, aStreamId,
308
0
                          buffer.Get(), aProps)) {
309
0
      return -1;
310
0
    }
311
0
  }
312
0
313
0
  return 0;
314
0
}
315
316
ShmemBuffer
317
CamerasParent::GetBuffer(size_t aSize)
318
0
{
319
0
  return mShmemPool.GetIfAvailable(aSize);
320
0
}
321
322
void
323
CallbackHelper::OnFrame(const webrtc::VideoFrame& aVideoFrame)
324
0
{
325
0
  LOG_VERBOSE((__PRETTY_FUNCTION__));
326
0
  RefPtr<DeliverFrameRunnable> runnable = nullptr;
327
0
  // Get frame properties
328
0
  camera::VideoFrameProperties properties;
329
0
  VideoFrameUtils::InitFrameBufferProperties(aVideoFrame, properties);
330
0
  // Get a shared memory buffer to copy the frame data into
331
0
  ShmemBuffer shMemBuffer = mParent->GetBuffer(properties.bufferSize());
332
0
  if (!shMemBuffer.Valid()) {
333
0
    // Either we ran out of buffers or they're not the right size yet
334
0
    LOG(("Correctly sized Video shmem not available in DeliverFrame"));
335
0
    // We will do the copy into a(n extra) temporary buffer inside
336
0
    // the DeliverFrameRunnable constructor.
337
0
  } else {
338
0
    // Shared memory buffers of the right size are available, do the copy here.
339
0
    VideoFrameUtils::CopyVideoFrameBuffers(shMemBuffer.GetBytes(),
340
0
                                           properties.bufferSize(), aVideoFrame);
341
0
    runnable = new DeliverFrameRunnable(mParent, mCapEngine, mStreamId,
342
0
                                        std::move(shMemBuffer), properties);
343
0
  }
344
0
  if (!runnable) {
345
0
    runnable = new DeliverFrameRunnable(mParent, mCapEngine, mStreamId,
346
0
                                        aVideoFrame, properties);
347
0
  }
348
0
  MOZ_ASSERT(mParent);
349
0
  nsIEventTarget* target = mParent->GetBackgroundEventTarget();
350
0
  MOZ_ASSERT(target != nullptr);
351
0
  target->Dispatch(runnable, NS_DISPATCH_NORMAL);
352
0
}
353
354
mozilla::ipc::IPCResult
355
0
CamerasParent::RecvReleaseFrame(mozilla::ipc::Shmem&& s) {
356
0
  mShmemPool.Put(ShmemBuffer(s));
357
0
  return IPC_OK();
358
0
}
359
360
bool
361
CamerasParent::SetupEngine(CaptureEngine aCapEngine)
362
0
{
363
0
  LOG((__PRETTY_FUNCTION__));
364
0
  StaticRefPtr<VideoEngine>& engine = sEngines[aCapEngine];
365
0
366
0
  if (!engine) {
367
0
    UniquePtr<webrtc::CaptureDeviceInfo> captureDeviceInfo;
368
0
    auto config = MakeUnique<webrtc::Config>();
369
0
370
0
    switch (aCapEngine) {
371
0
      case ScreenEngine:
372
0
        captureDeviceInfo = MakeUnique<webrtc::CaptureDeviceInfo>(
373
0
            webrtc::CaptureDeviceType::Screen);
374
0
        break;
375
0
      case BrowserEngine:
376
0
        captureDeviceInfo = MakeUnique<webrtc::CaptureDeviceInfo>(
377
0
            webrtc::CaptureDeviceType::Browser);
378
0
        break;
379
0
      case WinEngine:
380
0
        captureDeviceInfo = MakeUnique<webrtc::CaptureDeviceInfo>(
381
0
            webrtc::CaptureDeviceType::Window);
382
0
        break;
383
0
      case AppEngine:
384
0
        captureDeviceInfo = MakeUnique<webrtc::CaptureDeviceInfo>(
385
0
            webrtc::CaptureDeviceType::Application);
386
0
        break;
387
0
      case CameraEngine:
388
0
        captureDeviceInfo = MakeUnique<webrtc::CaptureDeviceInfo>(
389
0
            webrtc::CaptureDeviceType::Camera);
390
0
        break;
391
0
      default:
392
0
        LOG(("Invalid webrtc Video engine"));
393
0
        MOZ_CRASH();
394
0
        break;
395
0
    }
396
0
397
0
    config->Set<webrtc::CaptureDeviceInfo>(captureDeviceInfo.release());
398
0
    engine = VideoEngine::Create(std::move(config));
399
0
400
0
    if (!engine) {
401
0
      LOG(("VideoEngine::Create failed"));
402
0
      return false;
403
0
    }
404
0
  }
405
0
406
0
  if (aCapEngine == CameraEngine && !mCameraObserver) {
407
0
    mCameraObserver = new InputObserver(this);
408
0
    auto device_info = engine->GetOrCreateVideoCaptureDeviceInfo();
409
0
    MOZ_ASSERT(device_info);
410
0
    if (device_info) {
411
0
      device_info->RegisterVideoInputFeedBack(mCameraObserver);
412
0
    }
413
0
  }
414
0
415
0
  return true;
416
0
}
417
418
void
419
CamerasParent::CloseEngines()
420
0
{
421
0
  LOG((__PRETTY_FUNCTION__));
422
0
  if (!mWebRTCAlive) {
423
0
    return;
424
0
  }
425
0
  MOZ_ASSERT(sVideoCaptureThread->thread_id() == PlatformThread::CurrentId());
426
0
427
0
  // Stop the callers
428
0
  while (mCallbacks.Length()) {
429
0
    auto capEngine = mCallbacks[0]->mCapEngine;
430
0
    auto streamNum = mCallbacks[0]->mStreamId;
431
0
    LOG(("Forcing shutdown of engine %d, capturer %d", capEngine, streamNum));
432
0
    StopCapture(capEngine, streamNum);
433
0
    Unused << ReleaseCaptureDevice(capEngine, streamNum);
434
0
  }
435
0
436
0
  StaticRefPtr<VideoEngine>& engine = sEngines[CameraEngine];
437
0
  if (engine && mCameraObserver) {
438
0
    auto device_info = engine->GetOrCreateVideoCaptureDeviceInfo();
439
0
    MOZ_ASSERT(device_info);
440
0
    if (device_info) {
441
0
      device_info->DeRegisterVideoInputFeedBack(mCameraObserver);
442
0
    }
443
0
    mCameraObserver = nullptr;
444
0
  }
445
0
446
0
  // CloseEngines() is protected by sThreadMonitor
447
0
  sNumOfOpenCamerasParentEngines--;
448
0
  if (sNumOfOpenCamerasParentEngines == 0) {
449
0
    for (StaticRefPtr<VideoEngine>& engine : sEngines) {
450
0
      if (engine) {
451
0
        VideoEngine::Delete(engine);
452
0
        engine = nullptr;
453
0
      }
454
0
    }
455
0
  }
456
0
457
0
  mWebRTCAlive = false;
458
0
}
459
460
VideoEngine*
461
CamerasParent::EnsureInitialized(int aEngine)
462
0
{
463
0
  LOG_VERBOSE((__PRETTY_FUNCTION__));
464
0
  // We're shutting down, don't try to do new WebRTC ops.
465
0
  if (!mWebRTCAlive) {
466
0
    return nullptr;
467
0
  }
468
0
  CaptureEngine capEngine = static_cast<CaptureEngine>(aEngine);
469
0
  if (!SetupEngine(capEngine)) {
470
0
    LOG(("CamerasParent failed to initialize engine"));
471
0
    return nullptr;
472
0
  }
473
0
474
0
  return sEngines[aEngine];
475
0
}
476
477
// Dispatch the runnable to do the camera operation on the
478
// specific Cameras thread, preventing us from blocking, and
479
// chain a runnable to send back the result on the IPC thread.
480
// It would be nice to get rid of the code duplication here,
481
// perhaps via Promises.
482
mozilla::ipc::IPCResult
483
CamerasParent::RecvNumberOfCaptureDevices(const CaptureEngine& aCapEngine)
484
0
{
485
0
  LOG((__PRETTY_FUNCTION__));
486
0
  LOG(("CaptureEngine=%d", aCapEngine));
487
0
  RefPtr<CamerasParent> self(this);
488
0
  RefPtr<Runnable> webrtc_runnable =
489
0
    media::NewRunnableFrom([self, aCapEngine]() -> nsresult {
490
0
      int num = -1;
491
0
      if (auto engine = self->EnsureInitialized(aCapEngine)) {
492
0
        if (auto devInfo = engine->GetOrCreateVideoCaptureDeviceInfo()) {
493
0
          num = devInfo->NumberOfDevices();
494
0
        }
495
0
      }
496
0
      RefPtr<nsIRunnable> ipc_runnable =
497
0
        media::NewRunnableFrom([self, num]() -> nsresult {
498
0
          if (!self->mChildIsAlive) {
499
0
            return NS_ERROR_FAILURE;
500
0
          }
501
0
502
0
          if (num < 0) {
503
0
            LOG(("RecvNumberOfCaptureDevices couldn't find devices"));
504
0
            Unused << self->SendReplyFailure();
505
0
            return NS_ERROR_FAILURE;
506
0
          }
507
0
508
0
          LOG(("RecvNumberOfCaptureDevices: %d", num));
509
0
          Unused << self->SendReplyNumberOfCaptureDevices(num);
510
0
          return NS_OK;
511
0
        });
512
0
        self->mPBackgroundEventTarget->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
513
0
      return NS_OK;
514
0
    });
515
0
  DispatchToVideoCaptureThread(webrtc_runnable);
516
0
  return IPC_OK();
517
0
}
518
519
mozilla::ipc::IPCResult
520
CamerasParent::RecvEnsureInitialized(const CaptureEngine& aCapEngine)
521
0
{
522
0
  LOG((__PRETTY_FUNCTION__));
523
0
524
0
  RefPtr<CamerasParent> self(this);
525
0
  RefPtr<Runnable> webrtc_runnable =
526
0
    media::NewRunnableFrom([self, aCapEngine]() -> nsresult {
527
0
      bool result = self->EnsureInitialized(aCapEngine);
528
0
529
0
      RefPtr<nsIRunnable> ipc_runnable =
530
0
        media::NewRunnableFrom([self, result]() -> nsresult {
531
0
          if (!self->mChildIsAlive) {
532
0
            return NS_ERROR_FAILURE;
533
0
          }
534
0
535
0
          if (!result) {
536
0
            LOG(("RecvEnsureInitialized failed"));
537
0
            Unused << self->SendReplyFailure();
538
0
            return NS_ERROR_FAILURE;
539
0
          }
540
0
541
0
          LOG(("RecvEnsureInitialized succeeded"));
542
0
          Unused << self->SendReplySuccess();
543
0
          return NS_OK;
544
0
        });
545
0
        self->mPBackgroundEventTarget->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
546
0
      return NS_OK;
547
0
    });
548
0
  DispatchToVideoCaptureThread(webrtc_runnable);
549
0
  return IPC_OK();
550
0
}
551
552
mozilla::ipc::IPCResult
553
CamerasParent::RecvNumberOfCapabilities(const CaptureEngine& aCapEngine,
554
                                        const nsCString& unique_id)
555
0
{
556
0
  LOG((__PRETTY_FUNCTION__));
557
0
  LOG(("Getting caps for %s", unique_id.get()));
558
0
559
0
  RefPtr<CamerasParent> self(this);
560
0
  RefPtr<Runnable> webrtc_runnable =
561
0
    media::NewRunnableFrom([self, unique_id, aCapEngine]() -> nsresult {
562
0
      int num = -1;
563
0
      if (auto engine = self->EnsureInitialized(aCapEngine)) {
564
0
        if (auto devInfo = engine->GetOrCreateVideoCaptureDeviceInfo()) {
565
0
          num = devInfo->NumberOfCapabilities(unique_id.get());
566
0
        }
567
0
      }
568
0
      RefPtr<nsIRunnable> ipc_runnable =
569
0
        media::NewRunnableFrom([self, num]() -> nsresult {
570
0
          if (!self->mChildIsAlive) {
571
0
            return NS_ERROR_FAILURE;
572
0
          }
573
0
574
0
          if (num < 0) {
575
0
            LOG(("RecvNumberOfCapabilities couldn't find capabilities"));
576
0
            Unused << self->SendReplyFailure();
577
0
            return NS_ERROR_FAILURE;
578
0
          }
579
0
580
0
          LOG(("RecvNumberOfCapabilities: %d", num));
581
0
          Unused << self->SendReplyNumberOfCapabilities(num);
582
0
          return NS_OK;
583
0
        });
584
0
      self->mPBackgroundEventTarget->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
585
0
      return NS_OK;
586
0
    });
587
0
  DispatchToVideoCaptureThread(webrtc_runnable);
588
0
  return IPC_OK();
589
0
}
590
591
mozilla::ipc::IPCResult
592
CamerasParent::RecvGetCaptureCapability(const CaptureEngine& aCapEngine,
593
                                        const nsCString& unique_id,
594
                                        const int& num)
595
0
{
596
0
  LOG((__PRETTY_FUNCTION__));
597
0
  LOG(("RecvGetCaptureCapability: %s %d", unique_id.get(), num));
598
0
599
0
  RefPtr<CamerasParent> self(this);
600
0
  RefPtr<Runnable> webrtc_runnable =
601
0
    media::NewRunnableFrom([self, unique_id, aCapEngine, num]() -> nsresult {
602
0
      webrtc::VideoCaptureCapability webrtcCaps;
603
0
      int error = -1;
604
0
      if (auto engine = self->EnsureInitialized(aCapEngine)) {
605
0
        if (auto devInfo = engine->GetOrCreateVideoCaptureDeviceInfo()){
606
0
          error = devInfo->GetCapability(unique_id.get(), num, webrtcCaps);
607
0
        }
608
0
609
0
        if (!error && aCapEngine == CameraEngine) {
610
0
          auto iter = self->mAllCandidateCapabilities.find(unique_id);
611
0
          if (iter == self->mAllCandidateCapabilities.end()) {
612
0
            std::map<uint32_t, webrtc::VideoCaptureCapability> candidateCapabilities;
613
0
            candidateCapabilities.emplace(num, webrtcCaps);
614
0
            self->mAllCandidateCapabilities.emplace(nsCString(unique_id), candidateCapabilities);
615
0
          } else {
616
0
            (iter->second).emplace(num, webrtcCaps);
617
0
          }
618
0
        }
619
0
      }
620
0
      RefPtr<nsIRunnable> ipc_runnable =
621
0
        media::NewRunnableFrom([self, webrtcCaps, error]() -> nsresult {
622
0
          if (!self->mChildIsAlive) {
623
0
            return NS_ERROR_FAILURE;
624
0
          }
625
0
          VideoCaptureCapability capCap(webrtcCaps.width,
626
0
                                   webrtcCaps.height,
627
0
                                   webrtcCaps.maxFPS,
628
0
                                   webrtcCaps.expectedCaptureDelay,
629
0
                                   webrtcCaps.rawType,
630
0
                                   webrtcCaps.codecType,
631
0
                                   webrtcCaps.interlaced);
632
0
          LOG(("Capability: %u %u %u %u %d %d",
633
0
               webrtcCaps.width,
634
0
               webrtcCaps.height,
635
0
               webrtcCaps.maxFPS,
636
0
               webrtcCaps.expectedCaptureDelay,
637
0
               webrtcCaps.rawType,
638
0
               webrtcCaps.codecType));
639
0
          if (error) {
640
0
            Unused << self->SendReplyFailure();
641
0
            return NS_ERROR_FAILURE;
642
0
          }
643
0
          Unused << self->SendReplyGetCaptureCapability(capCap);
644
0
          return NS_OK;
645
0
        });
646
0
      self->mPBackgroundEventTarget->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
647
0
      return NS_OK;
648
0
    });
649
0
  DispatchToVideoCaptureThread(webrtc_runnable);
650
0
  return IPC_OK();
651
0
}
652
653
mozilla::ipc::IPCResult
654
CamerasParent::RecvGetCaptureDevice(const CaptureEngine& aCapEngine,
655
                                    const int& aListNumber)
656
0
{
657
0
  LOG((__PRETTY_FUNCTION__));
658
0
659
0
  RefPtr<CamerasParent> self(this);
660
0
  RefPtr<Runnable> webrtc_runnable =
661
0
    media::NewRunnableFrom([self, aCapEngine, aListNumber]() -> nsresult {
662
0
      char deviceName[MediaEngineSource::kMaxDeviceNameLength];
663
0
      char deviceUniqueId[MediaEngineSource::kMaxUniqueIdLength];
664
0
      nsCString name;
665
0
      nsCString uniqueId;
666
0
      pid_t devicePid = 0;
667
0
      int error = -1;
668
0
      if (auto engine = self->EnsureInitialized(aCapEngine)) {
669
0
        if (auto devInfo = engine->GetOrCreateVideoCaptureDeviceInfo()) {
670
0
          error = devInfo->GetDeviceName(aListNumber, deviceName, sizeof(deviceName),
671
0
                                         deviceUniqueId, sizeof(deviceUniqueId),
672
0
                                         nullptr, 0,
673
0
                                         &devicePid);
674
0
        }
675
0
      }
676
0
      if (!error) {
677
0
        name.Assign(deviceName);
678
0
        uniqueId.Assign(deviceUniqueId);
679
0
      }
680
0
      RefPtr<nsIRunnable> ipc_runnable =
681
0
        media::NewRunnableFrom([self, error, name, uniqueId, devicePid]() {
682
0
          if (!self->mChildIsAlive) {
683
0
            return NS_ERROR_FAILURE;
684
0
          }
685
0
          if (error) {
686
0
            LOG(("GetCaptureDevice failed: %d", error));
687
0
            Unused << self->SendReplyFailure();
688
0
            return NS_ERROR_FAILURE;
689
0
          }
690
0
          bool scary = (devicePid == getpid());
691
0
692
0
          LOG(("Returning %s name %s id (pid = %d)%s", name.get(),
693
0
               uniqueId.get(), devicePid, (scary? " (scary)" : "")));
694
0
          Unused << self->SendReplyGetCaptureDevice(name, uniqueId, scary);
695
0
          return NS_OK;
696
0
        });
697
0
      self->mPBackgroundEventTarget->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
698
0
      return NS_OK;
699
0
    });
700
0
  DispatchToVideoCaptureThread(webrtc_runnable);
701
0
  return IPC_OK();
702
0
}
703
704
// Find out whether the given principal has permission to use the
705
// camera. If the permission is not persistent, we'll make it
706
// a one-shot by removing the (session) permission.
707
static bool
708
HasCameraPermission(const ipc::PrincipalInfo& aPrincipalInfo)
709
0
{
710
0
  if (aPrincipalInfo.type() == ipc::PrincipalInfo::TNullPrincipalInfo) {
711
0
    return false;
712
0
  }
713
0
714
0
  if (aPrincipalInfo.type() == ipc::PrincipalInfo::TSystemPrincipalInfo) {
715
0
    return true;
716
0
  }
717
0
718
0
  MOZ_ASSERT(aPrincipalInfo.type() == ipc::PrincipalInfo::TContentPrincipalInfo);
719
0
720
0
  nsresult rv;
721
0
  nsCOMPtr<nsIPrincipal> principal =
722
0
    PrincipalInfoToPrincipal(aPrincipalInfo, &rv);
723
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
724
0
    return false;
725
0
  }
726
0
727
0
  // Name used with nsIPermissionManager
728
0
  static const char* cameraPermission = "MediaManagerVideo";
729
0
  nsCOMPtr<nsIPermissionManager> mgr =
730
0
    do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
731
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
732
0
    return false;
733
0
  }
734
0
735
0
  uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
736
0
  rv = mgr->TestExactPermissionFromPrincipal(principal, cameraPermission,
737
0
                                             &video);
738
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
739
0
    return false;
740
0
  }
741
0
742
0
  bool allowed = (video == nsIPermissionManager::ALLOW_ACTION);
743
0
744
0
  // Session permissions are removed after one use.
745
0
  if (allowed) {
746
0
    mgr->RemoveFromPrincipal(principal, cameraPermission);
747
0
  }
748
0
749
0
  return allowed;
750
0
}
751
752
mozilla::ipc::IPCResult
753
CamerasParent::RecvAllocateCaptureDevice(const CaptureEngine& aCapEngine,
754
                                         const nsCString& unique_id,
755
                                         const PrincipalInfo& aPrincipalInfo)
756
0
{
757
0
  LOG(("%s: Verifying permissions", __PRETTY_FUNCTION__));
758
0
  RefPtr<CamerasParent> self(this);
759
0
  RefPtr<Runnable> mainthread_runnable =
760
0
    media::NewRunnableFrom([self, aCapEngine, unique_id, aPrincipalInfo]() -> nsresult {
761
0
      // Verify whether the claimed origin has received permission
762
0
      // to use the camera, either persistently or this session (one shot).
763
0
      bool allowed = HasCameraPermission(aPrincipalInfo);
764
0
      if (!allowed) {
765
0
        // Developer preference for turning off permission check.
766
0
        if (Preferences::GetBool("media.navigator.permission.disabled",
767
0
                                 false) ||
768
0
            Preferences::GetBool("media.navigator.permission.fake")) {
769
0
          allowed = true;
770
0
          LOG(("No permission but checks are disabled or fake sources active"));
771
0
        } else {
772
0
          LOG(("No camera permission for this origin"));
773
0
        }
774
0
      }
775
0
      // After retrieving the permission (or not) on the main thread,
776
0
      // bounce to the WebRTC thread to allocate the device (or not),
777
0
      // then bounce back to the IPC thread for the reply to content.
778
0
      RefPtr<Runnable> webrtc_runnable =
779
0
      media::NewRunnableFrom([self, allowed, aCapEngine, unique_id]() -> nsresult {
780
0
        int numdev = -1;
781
0
        int error = -1;
782
0
        if (allowed && self->EnsureInitialized(aCapEngine)) {
783
0
          StaticRefPtr<VideoEngine>& engine = self->sEngines[aCapEngine];
784
0
          engine->CreateVideoCapture(numdev, unique_id.get());
785
0
          engine->WithEntry(numdev, [&error](VideoEngine::CaptureEntry& cap) {
786
0
            if (cap.VideoCapture()) {
787
0
              error = 0;
788
0
            }
789
0
          });
790
0
        }
791
0
        RefPtr<nsIRunnable> ipc_runnable =
792
0
          media::NewRunnableFrom([self, numdev, error]() -> nsresult {
793
0
            if (!self->mChildIsAlive) {
794
0
              return NS_ERROR_FAILURE;
795
0
            }
796
0
797
0
            if (error) {
798
0
              Unused << self->SendReplyFailure();
799
0
              return NS_ERROR_FAILURE;
800
0
            }
801
0
802
0
            LOG(("Allocated device nr %d", numdev));
803
0
            Unused << self->SendReplyAllocateCaptureDevice(numdev);
804
0
            return NS_OK;
805
0
          });
806
0
        self->mPBackgroundEventTarget->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
807
0
        return NS_OK;
808
0
        });
809
0
      self->DispatchToVideoCaptureThread(webrtc_runnable);
810
0
      return NS_OK;
811
0
    });
812
0
  NS_DispatchToMainThread(mainthread_runnable);
813
0
  return IPC_OK();
814
0
}
815
816
int
817
CamerasParent::ReleaseCaptureDevice(const CaptureEngine& aCapEngine,
818
                                    const int& capnum)
819
0
{
820
0
  int error = -1;
821
0
  if (auto engine = EnsureInitialized(aCapEngine)) {
822
0
    error = engine->ReleaseVideoCapture(capnum);
823
0
  }
824
0
  return error;
825
0
}
826
827
mozilla::ipc::IPCResult
828
CamerasParent::RecvReleaseCaptureDevice(const CaptureEngine& aCapEngine,
829
                                        const int& numdev)
830
0
{
831
0
  LOG((__PRETTY_FUNCTION__));
832
0
  LOG(("RecvReleaseCamera device nr %d", numdev));
833
0
834
0
  RefPtr<CamerasParent> self(this);
835
0
  RefPtr<Runnable> webrtc_runnable =
836
0
    media::NewRunnableFrom([self, aCapEngine, numdev]() -> nsresult {
837
0
      int error = self->ReleaseCaptureDevice(aCapEngine, numdev);
838
0
      RefPtr<nsIRunnable> ipc_runnable =
839
0
        media::NewRunnableFrom([self, error, numdev]() -> nsresult {
840
0
          if (!self->mChildIsAlive) {
841
0
            return NS_ERROR_FAILURE;
842
0
          }
843
0
844
0
          if (error) {
845
0
            Unused << self->SendReplyFailure();
846
0
            LOG(("Failed to free device nr %d", numdev));
847
0
            return NS_ERROR_FAILURE;
848
0
          }
849
0
850
0
          Unused << self->SendReplySuccess();
851
0
          LOG(("Freed device nr %d", numdev));
852
0
          return NS_OK;
853
0
        });
854
0
      self->mPBackgroundEventTarget->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
855
0
      return NS_OK;
856
0
    });
857
0
  DispatchToVideoCaptureThread(webrtc_runnable);
858
0
  return IPC_OK();
859
0
}
860
861
mozilla::ipc::IPCResult
862
CamerasParent::RecvStartCapture(const CaptureEngine& aCapEngine,
863
                                const int& capnum,
864
                                const VideoCaptureCapability& ipcCaps)
865
0
{
866
0
  LOG((__PRETTY_FUNCTION__));
867
0
868
0
  RefPtr<CamerasParent> self(this);
869
0
  RefPtr<Runnable> webrtc_runnable =
870
0
    media::NewRunnableFrom([self, aCapEngine, capnum, ipcCaps]() -> nsresult {
871
0
      LOG((__PRETTY_FUNCTION__));
872
0
      CallbackHelper** cbh;
873
0
      int error = -1;
874
0
      if (self->EnsureInitialized(aCapEngine)) {
875
0
        cbh = self->mCallbacks.AppendElement(
876
0
          new CallbackHelper(static_cast<CaptureEngine>(aCapEngine), capnum, self));
877
0
878
0
        self->sEngines[aCapEngine]->WithEntry(capnum,
879
0
          [&capnum, &aCapEngine, &error, &ipcCaps, &cbh, self]
880
0
          (VideoEngine::CaptureEntry& cap) {
881
0
          webrtc::VideoCaptureCapability capability;
882
0
          capability.width = ipcCaps.width();
883
0
          capability.height = ipcCaps.height();
884
0
          capability.maxFPS = ipcCaps.maxFPS();
885
0
          capability.expectedCaptureDelay = ipcCaps.expectedCaptureDelay();
886
0
          capability.rawType = static_cast<webrtc::RawVideoType>(ipcCaps.rawType());
887
0
          capability.codecType = static_cast<webrtc::VideoCodecType>(ipcCaps.codecType());
888
0
          capability.interlaced = ipcCaps.interlaced();
889
0
890
0
          MOZ_DIAGNOSTIC_ASSERT(sDeviceUniqueIDs.find(capnum) ==
891
0
                                sDeviceUniqueIDs.end());
892
0
          sDeviceUniqueIDs.emplace(capnum, cap.VideoCapture()->CurrentDeviceName());
893
0
894
0
          MOZ_DIAGNOSTIC_ASSERT(sAllRequestedCapabilities.find(capnum) ==
895
0
                                sAllRequestedCapabilities.end());
896
0
          sAllRequestedCapabilities.emplace(capnum, capability);
897
0
898
0
          if (aCapEngine == CameraEngine) {
899
0
            for (const auto &it : sDeviceUniqueIDs) {
900
0
              if (strcmp(it.second, cap.VideoCapture()->CurrentDeviceName()) == 0) {
901
0
                capability.width = std::max(
902
0
                  capability.width, sAllRequestedCapabilities[it.first].width);
903
0
                capability.height = std::max(
904
0
                  capability.height, sAllRequestedCapabilities[it.first].height);
905
0
                capability.maxFPS = std::max(
906
0
                  capability.maxFPS, sAllRequestedCapabilities[it.first].maxFPS);
907
0
              }
908
0
            }
909
0
910
0
            auto candidateCapabilities = self->mAllCandidateCapabilities.find(
911
0
              nsCString(cap.VideoCapture()->CurrentDeviceName()));
912
0
            if ((candidateCapabilities != self->mAllCandidateCapabilities.end()) &&
913
0
                (!candidateCapabilities->second.empty())) {
914
0
              int32_t minIdx = -1;
915
0
              uint64_t minDistance = UINT64_MAX;
916
0
917
0
              for (auto & candidateCapability : candidateCapabilities->second) {
918
0
                if (candidateCapability.second.rawType != capability.rawType) {
919
0
                  continue;
920
0
                }
921
0
                // The first priority is finding a suitable resolution.
922
0
                // So here we raise the weight of width and height
923
0
                uint64_t distance =
924
0
                  uint64_t(ResolutionFeasibilityDistance(
925
0
                    candidateCapability.second.width, capability.width)) +
926
0
                  uint64_t(ResolutionFeasibilityDistance(
927
0
                    candidateCapability.second.height, capability.height)) +
928
0
                  uint64_t(FeasibilityDistance(
929
0
                    candidateCapability.second.maxFPS, capability.maxFPS));
930
0
                if (distance < minDistance) {
931
0
                  minIdx = candidateCapability.first;
932
0
                  minDistance = distance;
933
0
                }
934
0
              }
935
0
              MOZ_ASSERT(minIdx != -1);
936
0
              capability = candidateCapabilities->second[minIdx];
937
0
            }
938
0
          } else if (aCapEngine == ScreenEngine ||
939
0
                     aCapEngine == BrowserEngine ||
940
0
                     aCapEngine == WinEngine ||
941
0
                     aCapEngine == AppEngine) {
942
0
            for (const auto &it : sDeviceUniqueIDs) {
943
0
              if (strcmp(it.second, cap.VideoCapture()->CurrentDeviceName()) == 0) {
944
0
                capability.maxFPS = std::max(
945
0
                  capability.maxFPS, sAllRequestedCapabilities[it.first].maxFPS);
946
0
              }
947
0
            }
948
0
          }
949
0
950
0
          error = cap.VideoCapture()->StartCapture(capability);
951
0
952
0
          if (!error) {
953
0
            cap.VideoCapture()->RegisterCaptureDataCallback(
954
0
              static_cast<rtc::VideoSinkInterface<webrtc::VideoFrame>*>(*cbh));
955
0
          } else {
956
0
            sDeviceUniqueIDs.erase(capnum);
957
0
            sAllRequestedCapabilities.erase(capnum);
958
0
          }
959
0
        });
960
0
      }
961
0
      RefPtr<nsIRunnable> ipc_runnable =
962
0
        media::NewRunnableFrom([self, error]() -> nsresult {
963
0
          if (!self->mChildIsAlive) {
964
0
            return NS_ERROR_FAILURE;
965
0
          }
966
0
967
0
          if (!error) {
968
0
            Unused << self->SendReplySuccess();
969
0
            return NS_OK;
970
0
          }
971
0
972
0
          Unused << self->SendReplyFailure();
973
0
          return NS_ERROR_FAILURE;
974
0
        });
975
0
      self->mPBackgroundEventTarget->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
976
0
      return NS_OK;
977
0
    });
978
0
  DispatchToVideoCaptureThread(webrtc_runnable);
979
0
  return IPC_OK();
980
0
}
981
982
mozilla::ipc::IPCResult
983
CamerasParent::RecvFocusOnSelectedSource(const CaptureEngine& aCapEngine,
984
                                         const int& aCapNum)
985
0
{
986
0
  LOG((__PRETTY_FUNCTION__));
987
0
  RefPtr<Runnable> webrtc_runnable =
988
0
    media::NewRunnableFrom([self = RefPtr<CamerasParent>(this),
989
0
                            aCapEngine, aCapNum]() -> nsresult {
990
0
      if (auto engine = self->EnsureInitialized(aCapEngine)) {
991
0
        engine->WithEntry(aCapNum, [self](VideoEngine::CaptureEntry& cap){
992
0
          if (cap.VideoCapture()) {
993
0
            bool result = cap.VideoCapture()->FocusOnSelectedSource();
994
0
            RefPtr<nsIRunnable> ipc_runnable =
995
0
              media::NewRunnableFrom([self, result]() -> nsresult {
996
0
                if (!self->mChildIsAlive) {
997
0
                  return NS_ERROR_FAILURE;
998
0
                }
999
0
1000
0
                if (result) {
1001
0
                  Unused << self->SendReplySuccess();
1002
0
                  return NS_OK;
1003
0
                }
1004
0
1005
0
                Unused << self->SendReplyFailure();
1006
0
                return NS_ERROR_FAILURE;
1007
0
              });
1008
0
            self->mPBackgroundEventTarget->Dispatch(ipc_runnable,
1009
0
                                                    NS_DISPATCH_NORMAL);
1010
0
          }
1011
0
        });
1012
0
      }
1013
0
      return NS_ERROR_FAILURE;
1014
0
  });
1015
0
  DispatchToVideoCaptureThread(webrtc_runnable);
1016
0
  return IPC_OK();
1017
0
}
1018
1019
void
1020
CamerasParent::StopCapture(const CaptureEngine& aCapEngine,
1021
                           const int& capnum)
1022
0
{
1023
0
  if (auto engine = EnsureInitialized(aCapEngine)) {
1024
0
    // we're removing elements, iterate backwards
1025
0
    for (size_t i = mCallbacks.Length(); i > 0; i--) {
1026
0
      if (mCallbacks[i - 1]->mCapEngine == aCapEngine &&
1027
0
          mCallbacks[i - 1]->mStreamId == (uint32_t)capnum) {
1028
0
1029
0
        CallbackHelper* cbh = mCallbacks[i-1];
1030
0
        engine->WithEntry(capnum,[cbh, &capnum](VideoEngine::CaptureEntry& cap){
1031
0
          if (cap.VideoCapture()) {
1032
0
            cap.VideoCapture()->DeRegisterCaptureDataCallback(
1033
0
              static_cast<rtc::VideoSinkInterface<webrtc::VideoFrame>*>(cbh));
1034
0
            cap.VideoCapture()->StopCaptureIfAllClientsClose();
1035
0
1036
0
            sDeviceUniqueIDs.erase(capnum);
1037
0
            sAllRequestedCapabilities.erase(capnum);
1038
0
          }
1039
0
        });
1040
0
1041
0
        delete mCallbacks[i - 1];
1042
0
        mCallbacks.RemoveElementAt(i - 1);
1043
0
        break;
1044
0
      }
1045
0
    }
1046
0
  }
1047
0
}
1048
1049
mozilla::ipc::IPCResult
1050
CamerasParent::RecvStopCapture(const CaptureEngine& aCapEngine,
1051
                               const int& capnum)
1052
0
{
1053
0
  LOG((__PRETTY_FUNCTION__));
1054
0
1055
0
  RefPtr<CamerasParent> self(this);
1056
0
  RefPtr<Runnable> webrtc_runnable =
1057
0
    media::NewRunnableFrom([self, aCapEngine, capnum]() -> nsresult {
1058
0
      self->StopCapture(aCapEngine, capnum);
1059
0
      return NS_OK;
1060
0
    });
1061
0
  nsresult rv = DispatchToVideoCaptureThread(webrtc_runnable);
1062
0
  if (!self->mChildIsAlive) {
1063
0
    if (NS_FAILED(rv)) {
1064
0
      return IPC_FAIL_NO_REASON(this);
1065
0
    }
1066
0
  } else {
1067
0
    if (NS_SUCCEEDED(rv)) {
1068
0
      if (!SendReplySuccess()) {
1069
0
        return IPC_FAIL_NO_REASON(this);
1070
0
      }
1071
0
    } else {
1072
0
      if (!SendReplyFailure()) {
1073
0
        return IPC_FAIL_NO_REASON(this);
1074
0
      }
1075
0
    }
1076
0
  }
1077
0
  return IPC_OK();
1078
0
}
1079
1080
void
1081
CamerasParent::StopIPC()
1082
0
{
1083
0
  MOZ_ASSERT(!mDestroyed);
1084
0
  // Release shared memory now, it's our last chance
1085
0
  mShmemPool.Cleanup(this);
1086
0
  // We don't want to receive callbacks or anything if we can't
1087
0
  // forward them anymore anyway.
1088
0
  mChildIsAlive = false;
1089
0
  mDestroyed = true;
1090
0
}
1091
1092
mozilla::ipc::IPCResult
1093
CamerasParent::RecvAllDone()
1094
0
{
1095
0
  LOG((__PRETTY_FUNCTION__));
1096
0
  // Don't try to send anything to the child now
1097
0
  mChildIsAlive = false;
1098
0
  IProtocol* mgr = Manager();
1099
0
  if (!Send__delete__(this)) {
1100
0
    return IPC_FAIL_NO_REASON(mgr);
1101
0
  }
1102
0
  return IPC_OK();
1103
0
}
1104
1105
void
1106
CamerasParent::ActorDestroy(ActorDestroyReason aWhy)
1107
0
{
1108
0
  // No more IPC from here
1109
0
  LOG((__PRETTY_FUNCTION__));
1110
0
  StopIPC();
1111
0
  // Shut down WebRTC (if we're not in full shutdown, else this
1112
0
  // will already have happened)
1113
0
  StopVideoCapture();
1114
0
}
1115
1116
CamerasParent::CamerasParent()
1117
  : mShmemPool(CaptureEngine::MaxEngine),
1118
    mChildIsAlive(true),
1119
    mDestroyed(false),
1120
    mWebRTCAlive(true)
1121
0
{
1122
0
  LOG(("CamerasParent: %p", this));
1123
0
  StaticMutexAutoLock slock(sMutex);
1124
0
1125
0
  if (sNumOfCamerasParents++ == 0) {
1126
0
    sThreadMonitor = new Monitor("CamerasParent::sThreadMonitor");
1127
0
  }
1128
0
1129
0
  mPBackgroundEventTarget = GetCurrentThreadSerialEventTarget();
1130
0
  MOZ_ASSERT(mPBackgroundEventTarget != nullptr,
1131
0
             "GetCurrentThreadEventTarget failed");
1132
0
1133
0
  LOG(("Spinning up WebRTC Cameras Thread"));
1134
0
1135
0
  RefPtr<CamerasParent> self(this);
1136
0
  RefPtr<Runnable> threadStart =
1137
0
    media::NewRunnableFrom([self]() -> nsresult {
1138
0
      // Register thread shutdown observer
1139
0
      nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1140
0
      if (NS_WARN_IF(!obs)) {
1141
0
        return NS_ERROR_FAILURE;
1142
0
      }
1143
0
      nsresult rv =
1144
0
        obs->AddObserver(self, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
1145
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1146
0
        return rv;
1147
0
      }
1148
0
      // Start the thread
1149
0
      MonitorAutoLock lock(*(self->sThreadMonitor));
1150
0
      if (self->sVideoCaptureThread == nullptr) {
1151
0
        MOZ_ASSERT(sNumOfOpenCamerasParentEngines == 0);
1152
0
        self->sVideoCaptureThread = new base::Thread("VideoCapture");
1153
0
        base::Thread::Options options;
1154
#if defined(_WIN32)
1155
        options.message_loop_type = MessageLoop::TYPE_MOZILLA_NONMAINUITHREAD;
1156
#else
1157
        options.message_loop_type = MessageLoop::TYPE_MOZILLA_NONMAINTHREAD;
1158
0
#endif
1159
0
        if (!self->sVideoCaptureThread->StartWithOptions(options)) {
1160
0
          MOZ_CRASH();
1161
0
        }
1162
0
      }
1163
0
      sNumOfOpenCamerasParentEngines++;
1164
0
      self->sThreadMonitor->NotifyAll();
1165
0
      return NS_OK;
1166
0
    });
1167
0
  NS_DispatchToMainThread(threadStart);
1168
0
}
1169
1170
CamerasParent::~CamerasParent()
1171
0
{
1172
0
  LOG(("~CamerasParent: %p", this));
1173
0
  StaticMutexAutoLock slock(sMutex);
1174
0
  if (--sNumOfCamerasParents == 0) {
1175
0
    delete sThreadMonitor;
1176
0
    sThreadMonitor = nullptr;
1177
0
  }
1178
0
}
1179
1180
already_AddRefed<CamerasParent>
1181
0
CamerasParent::Create() {
1182
0
  mozilla::ipc::AssertIsOnBackgroundThread();
1183
0
  return MakeAndAddRef<CamerasParent>();
1184
0
}
1185
1186
} // namespace camera
1187
} // namespace mozilla