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