Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "MediaEngineRemoteVideoSource.h"
7 :
8 : #include "mozilla/RefPtr.h"
9 : #include "VideoUtils.h"
10 : #include "nsIPrefService.h"
11 : #include "MediaTrackConstraints.h"
12 : #include "CamerasChild.h"
13 :
14 : extern mozilla::LogModule* GetMediaManagerLog();
15 : #define LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
16 : #define LOGFRAME(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Verbose, msg)
17 :
18 : namespace mozilla {
19 :
20 : // These need a definition somewhere because template
21 : // code is allowed to take their address, and they aren't
22 : // guaranteed to have one without this.
23 : const unsigned int MediaEngineSource::kMaxDeviceNameLength;
24 : const unsigned int MediaEngineSource::kMaxUniqueIdLength;;
25 :
26 : using dom::ConstrainLongRange;
27 :
28 0 : NS_IMPL_ISUPPORTS0(MediaEngineRemoteVideoSource)
29 :
30 0 : MediaEngineRemoteVideoSource::MediaEngineRemoteVideoSource(
31 : int aIndex, mozilla::camera::CaptureEngine aCapEngine,
32 0 : dom::MediaSourceEnum aMediaSource, bool aScary, const char* aMonitorName)
33 : : MediaEngineCameraVideoSource(aIndex, aMonitorName),
34 : mMediaSource(aMediaSource),
35 : mCapEngine(aCapEngine),
36 0 : mScary(aScary)
37 : {
38 0 : MOZ_ASSERT(aMediaSource != dom::MediaSourceEnum::Other);
39 0 : mSettings.mWidth.Construct(0);
40 0 : mSettings.mHeight.Construct(0);
41 0 : mSettings.mFrameRate.Construct(0);
42 0 : Init();
43 0 : }
44 :
45 : void
46 0 : MediaEngineRemoteVideoSource::Init()
47 : {
48 0 : LOG((__PRETTY_FUNCTION__));
49 : char deviceName[kMaxDeviceNameLength];
50 : char uniqueId[kMaxUniqueIdLength];
51 0 : if (mozilla::camera::GetChildAndCall(
52 : &mozilla::camera::CamerasChild::GetCaptureDevice,
53 : mCapEngine, mCaptureIndex,
54 : deviceName, kMaxDeviceNameLength,
55 : uniqueId, kMaxUniqueIdLength, nullptr)) {
56 0 : LOG(("Error initializing RemoteVideoSource (GetCaptureDevice)"));
57 0 : return;
58 : }
59 :
60 0 : SetName(NS_ConvertUTF8toUTF16(deviceName));
61 0 : SetUUID(uniqueId);
62 :
63 0 : mInitDone = true;
64 :
65 0 : return;
66 : }
67 :
68 : void
69 0 : MediaEngineRemoteVideoSource::Shutdown()
70 : {
71 0 : LOG((__PRETTY_FUNCTION__));
72 0 : if (!mInitDone) {
73 0 : return;
74 : }
75 0 : Super::Shutdown();
76 0 : if (mState == kStarted) {
77 : SourceMediaStream *source;
78 : bool empty;
79 :
80 : while (1) {
81 : {
82 0 : MonitorAutoLock lock(mMonitor);
83 0 : empty = mSources.IsEmpty();
84 0 : if (empty) {
85 0 : MOZ_ASSERT(mPrincipalHandles.IsEmpty());
86 0 : break;
87 : }
88 0 : source = mSources[0];
89 : }
90 0 : Stop(source, kVideoTrack); // XXX change to support multiple tracks
91 0 : }
92 0 : MOZ_ASSERT(mState == kStopped);
93 : }
94 :
95 0 : for (auto& registered : mRegisteredHandles) {
96 0 : MOZ_ASSERT(mState == kAllocated || mState == kStopped);
97 0 : Deallocate(registered.get());
98 : }
99 :
100 0 : MOZ_ASSERT(mState == kReleased);
101 0 : mInitDone = false;
102 0 : return;
103 : }
104 :
105 : nsresult
106 0 : MediaEngineRemoteVideoSource::Allocate(
107 : const dom::MediaTrackConstraints& aConstraints,
108 : const MediaEnginePrefs& aPrefs,
109 : const nsString& aDeviceId,
110 : const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
111 : AllocationHandle** aOutHandle,
112 : const char** aOutBadConstraint)
113 : {
114 0 : LOG((__PRETTY_FUNCTION__));
115 0 : AssertIsOnOwningThread();
116 :
117 0 : if (!mInitDone) {
118 0 : LOG(("Init not done"));
119 0 : return NS_ERROR_FAILURE;
120 : }
121 :
122 0 : nsresult rv = Super::Allocate(aConstraints, aPrefs, aDeviceId, aPrincipalInfo,
123 0 : aOutHandle, aOutBadConstraint);
124 0 : if (NS_FAILED(rv)) {
125 0 : return rv;
126 : }
127 0 : if (mState == kStarted &&
128 0 : MOZ_LOG_TEST(GetMediaManagerLog(), mozilla::LogLevel::Debug)) {
129 0 : MonitorAutoLock lock(mMonitor);
130 0 : if (mSources.IsEmpty()) {
131 0 : MOZ_ASSERT(mPrincipalHandles.IsEmpty());
132 0 : LOG(("Video device %d reallocated", mCaptureIndex));
133 : } else {
134 0 : LOG(("Video device %d allocated shared", mCaptureIndex));
135 : }
136 : }
137 0 : return NS_OK;
138 : }
139 :
140 : nsresult
141 0 : MediaEngineRemoteVideoSource::Deallocate(AllocationHandle* aHandle)
142 : {
143 0 : LOG((__PRETTY_FUNCTION__));
144 0 : AssertIsOnOwningThread();
145 :
146 0 : Super::Deallocate(aHandle);
147 :
148 0 : if (!mRegisteredHandles.Length()) {
149 0 : if (mState != kStopped && mState != kAllocated) {
150 0 : return NS_ERROR_FAILURE;
151 : }
152 0 : mozilla::camera::GetChildAndCall(
153 : &mozilla::camera::CamerasChild::ReleaseCaptureDevice,
154 0 : mCapEngine, mCaptureIndex);
155 0 : mState = kReleased;
156 0 : LOG(("Video device %d deallocated", mCaptureIndex));
157 : } else {
158 0 : LOG(("Video device %d deallocated but still in use", mCaptureIndex));
159 : }
160 0 : return NS_OK;
161 : }
162 :
163 : nsresult
164 0 : MediaEngineRemoteVideoSource::Start(SourceMediaStream* aStream, TrackID aID,
165 : const PrincipalHandle& aPrincipalHandle)
166 : {
167 0 : LOG((__PRETTY_FUNCTION__));
168 0 : AssertIsOnOwningThread();
169 0 : if (!mInitDone || !aStream) {
170 0 : LOG(("No stream or init not done"));
171 0 : return NS_ERROR_FAILURE;
172 : }
173 :
174 : {
175 0 : MonitorAutoLock lock(mMonitor);
176 0 : mSources.AppendElement(aStream);
177 0 : mPrincipalHandles.AppendElement(aPrincipalHandle);
178 0 : MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length());
179 : }
180 :
181 0 : aStream->AddTrack(aID, 0, new VideoSegment(), SourceMediaStream::ADDTRACK_QUEUED);
182 :
183 0 : if (mState == kStarted) {
184 0 : return NS_OK;
185 : }
186 : mImageContainer =
187 0 : layers::LayerManager::CreateImageContainer(layers::ImageContainer::ASYNCHRONOUS);
188 :
189 0 : mState = kStarted;
190 0 : mTrackID = aID;
191 :
192 0 : if (mozilla::camera::GetChildAndCall(
193 : &mozilla::camera::CamerasChild::StartCapture,
194 : mCapEngine, mCaptureIndex, mCapability, this)) {
195 0 : LOG(("StartCapture failed"));
196 0 : return NS_ERROR_FAILURE;
197 : }
198 :
199 0 : return NS_OK;
200 : }
201 :
202 : nsresult
203 0 : MediaEngineRemoteVideoSource::Stop(mozilla::SourceMediaStream* aSource,
204 : mozilla::TrackID aID)
205 : {
206 0 : LOG((__PRETTY_FUNCTION__));
207 0 : AssertIsOnOwningThread();
208 : {
209 0 : MonitorAutoLock lock(mMonitor);
210 :
211 : // Drop any cached image so we don't start with a stale image on next
212 : // usage
213 0 : mImage = nullptr;
214 :
215 0 : size_t i = mSources.IndexOf(aSource);
216 0 : if (i == mSources.NoIndex) {
217 : // Already stopped - this is allowed
218 0 : return NS_OK;
219 : }
220 :
221 0 : MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length());
222 0 : mSources.RemoveElementAt(i);
223 0 : mPrincipalHandles.RemoveElementAt(i);
224 :
225 0 : aSource->EndTrack(aID);
226 :
227 0 : if (!mSources.IsEmpty()) {
228 0 : return NS_OK;
229 : }
230 0 : if (mState != kStarted) {
231 0 : return NS_ERROR_FAILURE;
232 : }
233 :
234 0 : mState = kStopped;
235 : }
236 :
237 0 : mozilla::camera::GetChildAndCall(
238 : &mozilla::camera::CamerasChild::StopCapture,
239 0 : mCapEngine, mCaptureIndex);
240 :
241 0 : return NS_OK;
242 : }
243 :
244 : nsresult
245 0 : MediaEngineRemoteVideoSource::Restart(AllocationHandle* aHandle,
246 : const dom::MediaTrackConstraints& aConstraints,
247 : const MediaEnginePrefs& aPrefs,
248 : const nsString& aDeviceId,
249 : const char** aOutBadConstraint)
250 : {
251 0 : AssertIsOnOwningThread();
252 0 : if (!mInitDone) {
253 0 : LOG(("Init not done"));
254 0 : return NS_ERROR_FAILURE;
255 : }
256 0 : MOZ_ASSERT(aHandle);
257 0 : NormalizedConstraints constraints(aConstraints);
258 0 : return ReevaluateAllocation(aHandle, &constraints, aPrefs, aDeviceId,
259 0 : aOutBadConstraint);
260 : }
261 :
262 : nsresult
263 0 : MediaEngineRemoteVideoSource::UpdateSingleSource(
264 : const AllocationHandle* aHandle,
265 : const NormalizedConstraints& aNetConstraints,
266 : const MediaEnginePrefs& aPrefs,
267 : const nsString& aDeviceId,
268 : const char** aOutBadConstraint)
269 : {
270 0 : if (!ChooseCapability(aNetConstraints, aPrefs, aDeviceId)) {
271 0 : *aOutBadConstraint = FindBadConstraint(aNetConstraints, *this, aDeviceId);
272 0 : return NS_ERROR_FAILURE;
273 : }
274 :
275 0 : switch (mState) {
276 : case kReleased:
277 0 : MOZ_ASSERT(aHandle);
278 0 : if (camera::GetChildAndCall(&camera::CamerasChild::AllocateCaptureDevice,
279 0 : mCapEngine, GetUUID().get(),
280 : kMaxUniqueIdLength, mCaptureIndex,
281 : aHandle->mPrincipalInfo)) {
282 0 : return NS_ERROR_FAILURE;
283 : }
284 0 : mState = kAllocated;
285 0 : SetLastCapability(mCapability);
286 0 : LOG(("Video device %d allocated", mCaptureIndex));
287 0 : break;
288 :
289 : case kStarted:
290 0 : if (mCapability != mLastCapability) {
291 0 : camera::GetChildAndCall(&camera::CamerasChild::StopCapture,
292 0 : mCapEngine, mCaptureIndex);
293 0 : if (camera::GetChildAndCall(&camera::CamerasChild::StartCapture,
294 : mCapEngine, mCaptureIndex, mCapability,
295 : this)) {
296 0 : LOG(("StartCapture failed"));
297 0 : return NS_ERROR_FAILURE;
298 : }
299 0 : SetLastCapability(mCapability);
300 : }
301 0 : break;
302 :
303 : default:
304 0 : LOG(("Video device %d in ignored state %d", mCaptureIndex, mState));
305 0 : break;
306 : }
307 0 : return NS_OK;
308 : }
309 :
310 : void
311 0 : MediaEngineRemoteVideoSource::SetLastCapability(
312 : const webrtc::CaptureCapability& aCapability)
313 : {
314 0 : mLastCapability = mCapability;
315 :
316 0 : webrtc::CaptureCapability cap = aCapability;
317 0 : switch (mMediaSource) {
318 : case dom::MediaSourceEnum::Screen:
319 : case dom::MediaSourceEnum::Window:
320 : case dom::MediaSourceEnum::Application:
321 : // Undo the hack where ideal and max constraints are crammed together
322 : // in mCapability for consumption by low-level code. We don't actually
323 : // know the real resolution yet, so report min(ideal, max) for now.
324 0 : cap.width = std::min(cap.width >> 16, cap.width & 0xffff);
325 0 : cap.height = std::min(cap.height >> 16, cap.height & 0xffff);
326 0 : break;
327 :
328 : default:
329 0 : break;
330 : }
331 0 : RefPtr<MediaEngineRemoteVideoSource> that = this;
332 :
333 0 : NS_DispatchToMainThread(media::NewRunnableFrom([that, cap]() mutable {
334 0 : that->mSettings.mWidth.Value() = cap.width;
335 0 : that->mSettings.mHeight.Value() = cap.height;
336 0 : that->mSettings.mFrameRate.Value() = cap.maxFPS;
337 0 : return NS_OK;
338 0 : }));
339 0 : }
340 :
341 : void
342 0 : MediaEngineRemoteVideoSource::NotifyPull(MediaStreamGraph* aGraph,
343 : SourceMediaStream* aSource,
344 : TrackID aID, StreamTime aDesiredTime,
345 : const PrincipalHandle& aPrincipalHandle)
346 : {
347 0 : VideoSegment segment;
348 :
349 0 : MonitorAutoLock lock(mMonitor);
350 0 : if (mState != kStarted) {
351 0 : return;
352 : }
353 :
354 0 : StreamTime delta = aDesiredTime - aSource->GetEndOfAppendedData(aID);
355 :
356 0 : if (delta > 0) {
357 : // nullptr images are allowed
358 0 : AppendToTrack(aSource, mImage, aID, delta, aPrincipalHandle);
359 : }
360 : }
361 :
362 : void
363 0 : MediaEngineRemoteVideoSource::FrameSizeChange(unsigned int w, unsigned int h)
364 : {
365 : #if defined(MOZ_WIDGET_GONK)
366 : mMonitor.AssertCurrentThreadOwns(); // mWidth and mHeight are protected...
367 : #endif
368 0 : if ((mWidth < 0) || (mHeight < 0) ||
369 0 : (w != (unsigned int) mWidth) || (h != (unsigned int) mHeight)) {
370 0 : LOG(("MediaEngineRemoteVideoSource Video FrameSizeChange: %ux%u was %ux%u", w, h, mWidth, mHeight));
371 0 : mWidth = w;
372 0 : mHeight = h;
373 :
374 0 : RefPtr<MediaEngineRemoteVideoSource> that = this;
375 0 : NS_DispatchToMainThread(media::NewRunnableFrom([that, w, h]() mutable {
376 0 : that->mSettings.mWidth.Value() = w;
377 0 : that->mSettings.mHeight.Value() = h;
378 0 : return NS_OK;
379 0 : }));
380 : }
381 0 : }
382 :
383 : int
384 0 : MediaEngineRemoteVideoSource::DeliverFrame(uint8_t* aBuffer ,
385 : const camera::VideoFrameProperties& aProps)
386 : {
387 : // Check for proper state.
388 0 : if (mState != kStarted) {
389 0 : LOG(("DeliverFrame: video not started"));
390 0 : return 0;
391 : }
392 :
393 : // Update the dimensions
394 0 : FrameSizeChange(aProps.width(), aProps.height());
395 :
396 : // Create a video frame and append it to the track.
397 0 : RefPtr<layers::PlanarYCbCrImage> image = mImageContainer->CreatePlanarYCbCrImage();
398 :
399 0 : uint8_t* frame = static_cast<uint8_t*> (aBuffer);
400 0 : const uint8_t lumaBpp = 8;
401 0 : const uint8_t chromaBpp = 4;
402 :
403 : // Take lots of care to round up!
404 0 : layers::PlanarYCbCrData data;
405 0 : data.mYChannel = frame;
406 0 : data.mYSize = IntSize(mWidth, mHeight);
407 0 : data.mYStride = (mWidth * lumaBpp + 7)/ 8;
408 0 : data.mCbCrStride = (mWidth * chromaBpp + 7) / 8;
409 0 : data.mCbChannel = frame + mHeight * data.mYStride;
410 0 : data.mCrChannel = data.mCbChannel + ((mHeight+1)/2) * data.mCbCrStride;
411 0 : data.mCbCrSize = IntSize((mWidth+1)/ 2, (mHeight+1)/ 2);
412 0 : data.mPicX = 0;
413 0 : data.mPicY = 0;
414 0 : data.mPicSize = IntSize(mWidth, mHeight);
415 0 : data.mStereoMode = StereoMode::MONO;
416 :
417 0 : if (!image->CopyData(data)) {
418 0 : MOZ_ASSERT(false);
419 : return 0;
420 : }
421 :
422 : #ifdef DEBUG
423 : static uint32_t frame_num = 0;
424 0 : LOGFRAME(("frame %d (%dx%d); timeStamp %u, ntpTimeMs %" PRIu64 ", renderTimeMs %" PRIu64,
425 : frame_num++, mWidth, mHeight,
426 : aProps.timeStamp(), aProps.ntpTimeMs(), aProps.renderTimeMs()));
427 : #endif
428 :
429 : // we don't touch anything in 'this' until here (except for snapshot,
430 : // which has it's own lock)
431 0 : MonitorAutoLock lock(mMonitor);
432 :
433 : // implicitly releases last image
434 0 : mImage = image.forget();
435 :
436 : // We'll push the frame into the MSG on the next NotifyPull. This will avoid
437 : // swamping the MSG with frames should it be taking longer than normal to run
438 : // an iteration.
439 :
440 0 : return 0;
441 : }
442 :
443 : size_t
444 0 : MediaEngineRemoteVideoSource::NumCapabilities() const
445 : {
446 0 : mHardcodedCapabilities.Clear();
447 0 : int num = mozilla::camera::GetChildAndCall(
448 : &mozilla::camera::CamerasChild::NumberOfCapabilities,
449 : mCapEngine,
450 0 : GetUUID().get());
451 0 : if (num < 1) {
452 : // The default for devices that don't return discrete capabilities: treat
453 : // them as supporting all capabilities orthogonally. E.g. screensharing.
454 : // CaptureCapability defaults key values to 0, which means accept any value.
455 0 : mHardcodedCapabilities.AppendElement(webrtc::CaptureCapability());
456 0 : num = mHardcodedCapabilities.Length(); // 1
457 : }
458 0 : return num;
459 : }
460 :
461 : bool
462 0 : MediaEngineRemoteVideoSource::ChooseCapability(
463 : const NormalizedConstraints &aConstraints,
464 : const MediaEnginePrefs &aPrefs,
465 : const nsString& aDeviceId)
466 : {
467 0 : AssertIsOnOwningThread();
468 :
469 0 : switch(mMediaSource) {
470 : case dom::MediaSourceEnum::Screen:
471 : case dom::MediaSourceEnum::Window:
472 : case dom::MediaSourceEnum::Application: {
473 0 : FlattenedConstraints c(aConstraints);
474 : // The actual resolution to constrain around is not easy to find ahead of
475 : // time (and may in fact change over time), so as a hack, we push ideal
476 : // and max constraints down to desktop_capture_impl.cc and finish the
477 : // algorithm there.
478 0 : mCapability.width = (c.mWidth.mIdeal.valueOr(0) & 0xffff) << 16 |
479 0 : (c.mWidth.mMax & 0xffff);
480 0 : mCapability.height = (c.mHeight.mIdeal.valueOr(0) & 0xffff) << 16 |
481 0 : (c.mHeight.mMax & 0xffff);
482 0 : mCapability.maxFPS = c.mFrameRate.Clamp(c.mFrameRate.mIdeal.valueOr(aPrefs.mFPS));
483 0 : return true;
484 : }
485 : default:
486 0 : return MediaEngineCameraVideoSource::ChooseCapability(aConstraints, aPrefs, aDeviceId);
487 : }
488 :
489 : }
490 :
491 : void
492 0 : MediaEngineRemoteVideoSource::GetCapability(size_t aIndex,
493 : webrtc::CaptureCapability& aOut) const
494 : {
495 0 : if (!mHardcodedCapabilities.IsEmpty()) {
496 0 : MediaEngineCameraVideoSource::GetCapability(aIndex, aOut);
497 : }
498 0 : mozilla::camera::GetChildAndCall(
499 : &mozilla::camera::CamerasChild::GetCaptureCapability,
500 : mCapEngine,
501 0 : GetUUID().get(),
502 : aIndex,
503 0 : aOut);
504 0 : }
505 :
506 0 : void MediaEngineRemoteVideoSource::Refresh(int aIndex) {
507 : // NOTE: mCaptureIndex might have changed when allocated!
508 : // Use aIndex to update information, but don't change mCaptureIndex!!
509 : // Caller looked up this source by uniqueId, so it shouldn't change
510 : char deviceName[kMaxDeviceNameLength];
511 : char uniqueId[kMaxUniqueIdLength];
512 :
513 0 : if (mozilla::camera::GetChildAndCall(
514 : &mozilla::camera::CamerasChild::GetCaptureDevice,
515 : mCapEngine, aIndex,
516 0 : deviceName, sizeof(deviceName),
517 0 : uniqueId, sizeof(uniqueId), nullptr)) {
518 0 : return;
519 : }
520 :
521 0 : SetName(NS_ConvertUTF8toUTF16(deviceName));
522 : #ifdef DEBUG
523 0 : MOZ_ASSERT(GetUUID().Equals(uniqueId));
524 : #endif
525 : }
526 :
527 : }
|