/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 | | } |