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