1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include "MediaEngineDefault.h"
6
7 #include "ImageContainer.h"
8 #include "ImageTypes.h"
9 #include "Layers.h"
10 #include "MediaStreamGraph.h"
11 #include "MediaTrackConstraints.h"
12 #include "mozilla/dom/File.h"
13 #include "mozilla/UniquePtr.h"
14 #include "nsCOMPtr.h"
15 #include "nsContentUtils.h"
16 #include "nsIFile.h"
17 #include "nsIFilePicker.h"
18 #include "nsIPrefBranch.h"
19 #include "nsIPrefService.h"
20
21 #ifdef MOZ_WIDGET_ANDROID
22 #include "nsISupportsUtils.h"
23 #endif
24
25 #ifdef MOZ_WEBRTC
26 #include "YuvStamper.h"
27 #endif
28
29 #define DEFAULT_AUDIO_TIMER_MS 10
30 namespace mozilla {
31
32 using namespace mozilla::gfx;
33
34 /**
35 * Default video source.
36 */
37
MediaEngineDefaultVideoSource()38 MediaEngineDefaultVideoSource::MediaEngineDefaultVideoSource()
39 : mTimer(nullptr), mMutex("MediaEngineDefaultVideoSource::mMutex") {}
40
~MediaEngineDefaultVideoSource()41 MediaEngineDefaultVideoSource::~MediaEngineDefaultVideoSource() {}
42
GetName() const43 nsString MediaEngineDefaultVideoSource::GetName() const {
44 return NS_LITERAL_STRING(u"Default Video Device");
45 }
46
GetUUID() const47 nsCString MediaEngineDefaultVideoSource::GetUUID() const {
48 return NS_LITERAL_CSTRING("1041FCBD-3F12-4F7B-9E9B-1EC556DD5676");
49 }
50
GetBestFitnessDistance(const nsTArray<const NormalizedConstraintSet * > & aConstraintSets,const nsString & aDeviceId) const51 uint32_t MediaEngineDefaultVideoSource::GetBestFitnessDistance(
52 const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
53 const nsString& aDeviceId) const {
54 AssertIsOnOwningThread();
55
56 uint32_t distance = 0;
57 #ifdef MOZ_WEBRTC
58 for (const auto* cs : aConstraintSets) {
59 distance =
60 MediaConstraintsHelper::GetMinimumFitnessDistance(*cs, aDeviceId);
61 break; // distance is read from first entry only
62 }
63 #endif
64 return distance;
65 }
66
Allocate(const dom::MediaTrackConstraints & aConstraints,const MediaEnginePrefs & aPrefs,const nsString & aDeviceId,const mozilla::ipc::PrincipalInfo & aPrincipalInfo,AllocationHandle ** aOutHandle,const char ** aOutBadConstraint)67 nsresult MediaEngineDefaultVideoSource::Allocate(
68 const dom::MediaTrackConstraints& aConstraints,
69 const MediaEnginePrefs& aPrefs, const nsString& aDeviceId,
70 const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
71 AllocationHandle** aOutHandle, const char** aOutBadConstraint) {
72 AssertIsOnOwningThread();
73
74 MOZ_ASSERT(mState == kReleased);
75
76 FlattenedConstraints c(aConstraints);
77
78 // Mock failure for automated tests.
79 if (c.mDeviceId.mIdeal.find(NS_LITERAL_STRING("bad device")) !=
80 c.mDeviceId.mIdeal.end()) {
81 return NS_ERROR_FAILURE;
82 }
83
84 // emulator debug is very, very slow; reduce load on it with smaller/slower
85 // fake video
86 mOpts = aPrefs;
87 mOpts.mWidth =
88 c.mWidth.Get(aPrefs.mWidth ? aPrefs.mWidth :
89 #ifdef DEBUG
90 MediaEnginePrefs::DEFAULT_43_VIDEO_WIDTH / 2
91 #else
92 MediaEnginePrefs::DEFAULT_43_VIDEO_WIDTH
93 #endif
94 );
95 mOpts.mHeight =
96 c.mHeight.Get(aPrefs.mHeight ? aPrefs.mHeight :
97 #ifdef DEBUG
98 MediaEnginePrefs::DEFAULT_43_VIDEO_HEIGHT / 2
99 #else
100 MediaEnginePrefs::DEFAULT_43_VIDEO_HEIGHT
101 #endif
102 );
103 mOpts.mWidth = std::max(160, std::min(mOpts.mWidth, 4096)) & ~1;
104 mOpts.mHeight = std::max(90, std::min(mOpts.mHeight, 2160)) & ~1;
105 *aOutHandle = nullptr;
106
107 MutexAutoLock lock(mMutex);
108 mState = kAllocated;
109 return NS_OK;
110 }
111
Deallocate(const RefPtr<const AllocationHandle> & aHandle)112 nsresult MediaEngineDefaultVideoSource::Deallocate(
113 const RefPtr<const AllocationHandle>& aHandle) {
114 AssertIsOnOwningThread();
115
116 MOZ_ASSERT(!aHandle);
117 MOZ_ASSERT(!mImage);
118 MOZ_ASSERT(mState == kStopped || mState == kAllocated);
119
120 MutexAutoLock lock(mMutex);
121 if (mStream && IsTrackIDExplicit(mTrackID)) {
122 mStream->EndTrack(mTrackID);
123 mStream = nullptr;
124 mTrackID = TRACK_NONE;
125 }
126 mState = kReleased;
127 mImageContainer = nullptr;
128
129 return NS_OK;
130 }
131
AllocateSolidColorFrame(layers::PlanarYCbCrData & aData,int aWidth,int aHeight,int aY,int aCb,int aCr)132 static void AllocateSolidColorFrame(layers::PlanarYCbCrData& aData, int aWidth,
133 int aHeight, int aY, int aCb, int aCr) {
134 MOZ_ASSERT(!(aWidth & 1));
135 MOZ_ASSERT(!(aHeight & 1));
136 // Allocate a single frame with a solid color
137 int yLen = aWidth * aHeight;
138 int cbLen = yLen >> 2;
139 int crLen = cbLen;
140 uint8_t* frame = (uint8_t*)malloc(yLen + cbLen + crLen);
141 memset(frame, aY, yLen);
142 memset(frame + yLen, aCb, cbLen);
143 memset(frame + yLen + cbLen, aCr, crLen);
144
145 aData.mYChannel = frame;
146 aData.mYSize = IntSize(aWidth, aHeight);
147 aData.mYStride = aWidth;
148 aData.mCbCrStride = aWidth >> 1;
149 aData.mCbChannel = frame + yLen;
150 aData.mCrChannel = aData.mCbChannel + cbLen;
151 aData.mCbCrSize = IntSize(aWidth >> 1, aHeight >> 1);
152 aData.mPicX = 0;
153 aData.mPicY = 0;
154 aData.mPicSize = IntSize(aWidth, aHeight);
155 aData.mStereoMode = StereoMode::MONO;
156 }
157
ReleaseFrame(layers::PlanarYCbCrData & aData)158 static void ReleaseFrame(layers::PlanarYCbCrData& aData) {
159 free(aData.mYChannel);
160 }
161
SetTrack(const RefPtr<const AllocationHandle> & aHandle,const RefPtr<SourceMediaStream> & aStream,TrackID aTrackID,const PrincipalHandle & aPrincipal)162 nsresult MediaEngineDefaultVideoSource::SetTrack(
163 const RefPtr<const AllocationHandle>& aHandle,
164 const RefPtr<SourceMediaStream>& aStream, TrackID aTrackID,
165 const PrincipalHandle& aPrincipal) {
166 AssertIsOnOwningThread();
167
168 MOZ_ASSERT(mState == kAllocated);
169 MOZ_ASSERT(!mStream);
170 MOZ_ASSERT(mTrackID == TRACK_NONE);
171
172 {
173 MutexAutoLock lock(mMutex);
174 mStream = aStream;
175 mTrackID = aTrackID;
176 }
177 aStream->AddTrack(aTrackID, 0, new VideoSegment(),
178 SourceMediaStream::ADDTRACK_QUEUED);
179 return NS_OK;
180 }
181
Start(const RefPtr<const AllocationHandle> & aHandle)182 nsresult MediaEngineDefaultVideoSource::Start(
183 const RefPtr<const AllocationHandle>& aHandle) {
184 AssertIsOnOwningThread();
185
186 MOZ_ASSERT(mState == kAllocated || mState == kStopped);
187 MOZ_ASSERT(mStream, "SetTrack() must happen before Start()");
188 MOZ_ASSERT(IsTrackIDExplicit(mTrackID),
189 "SetTrack() must happen before Start()");
190
191 mTimer = NS_NewTimer();
192 if (!mTimer) {
193 return NS_ERROR_FAILURE;
194 }
195
196 if (!mImageContainer) {
197 mImageContainer = layers::LayerManager::CreateImageContainer(
198 layers::ImageContainer::ASYNCHRONOUS);
199 }
200
201 // Start timer for subsequent frames
202 uint32_t interval;
203 #if defined(MOZ_WIDGET_ANDROID) && defined(DEBUG)
204 // emulator debug is very, very slow and has problems dealing with realtime
205 // audio inputs
206 interval = 10 * (1000 / mOpts.mFPS);
207 #else
208 interval = 1000 / mOpts.mFPS;
209 #endif
210 mTimer->InitWithNamedFuncCallback(
211 [](nsITimer* aTimer, void* aClosure) {
212 RefPtr<MediaEngineDefaultVideoSource> source =
213 static_cast<MediaEngineDefaultVideoSource*>(aClosure);
214 source->GenerateFrame();
215 },
216 this, interval, nsITimer::TYPE_REPEATING_SLACK,
217 "MediaEngineDefaultVideoSource::GenerateFrame");
218
219 MutexAutoLock lock(mMutex);
220 mState = kStarted;
221 return NS_OK;
222 }
223
Stop(const RefPtr<const AllocationHandle> & aHandle)224 nsresult MediaEngineDefaultVideoSource::Stop(
225 const RefPtr<const AllocationHandle>& aHandle) {
226 AssertIsOnOwningThread();
227
228 if (mState == kStopped || mState == kAllocated) {
229 return NS_OK;
230 }
231
232 MOZ_ASSERT(mState == kStarted);
233 MOZ_ASSERT(mTimer);
234 MOZ_ASSERT(mStream);
235 MOZ_ASSERT(IsTrackIDExplicit(mTrackID));
236
237 mTimer->Cancel();
238 mTimer = nullptr;
239
240 MutexAutoLock lock(mMutex);
241
242 mImage = nullptr;
243 mState = kStopped;
244
245 return NS_OK;
246 }
247
Reconfigure(const RefPtr<AllocationHandle> & aHandle,const dom::MediaTrackConstraints & aConstraints,const MediaEnginePrefs & aPrefs,const nsString & aDeviceId,const char ** aOutBadConstraint)248 nsresult MediaEngineDefaultVideoSource::Reconfigure(
249 const RefPtr<AllocationHandle>& aHandle,
250 const dom::MediaTrackConstraints& aConstraints,
251 const MediaEnginePrefs& aPrefs, const nsString& aDeviceId,
252 const char** aOutBadConstraint) {
253 return NS_OK;
254 }
255
GenerateFrame()256 void MediaEngineDefaultVideoSource::GenerateFrame() {
257 AssertIsOnOwningThread();
258
259 // Update the target color
260 if (mCr <= 16) {
261 if (mCb < 240) {
262 mCb++;
263 } else {
264 mCr++;
265 }
266 } else if (mCb >= 240) {
267 if (mCr < 240) {
268 mCr++;
269 } else {
270 mCb--;
271 }
272 } else if (mCr >= 240) {
273 if (mCb > 16) {
274 mCb--;
275 } else {
276 mCr--;
277 }
278 } else {
279 mCr--;
280 }
281
282 // Allocate a single solid color image
283 RefPtr<layers::PlanarYCbCrImage> ycbcr_image =
284 mImageContainer->CreatePlanarYCbCrImage();
285 layers::PlanarYCbCrData data;
286 AllocateSolidColorFrame(data, mOpts.mWidth, mOpts.mHeight, 0x80, mCb, mCr);
287
288 #ifdef MOZ_WEBRTC
289 uint64_t timestamp = PR_Now();
290 YuvStamper::Encode(mOpts.mWidth, mOpts.mHeight, mOpts.mWidth, data.mYChannel,
291 reinterpret_cast<unsigned char*>(×tamp),
292 sizeof(timestamp), 0, 0);
293 #endif
294
295 bool setData = ycbcr_image->CopyData(data);
296 MOZ_ASSERT(setData);
297
298 // SetData copies data, so we can free the frame
299 ReleaseFrame(data);
300
301 if (!setData) {
302 return;
303 }
304
305 MutexAutoLock lock(mMutex);
306 mImage = Move(ycbcr_image);
307 }
308
Pull(const RefPtr<const AllocationHandle> & aHandle,const RefPtr<SourceMediaStream> & aStream,TrackID aTrackID,StreamTime aDesiredTime,const PrincipalHandle & aPrincipalHandle)309 void MediaEngineDefaultVideoSource::Pull(
310 const RefPtr<const AllocationHandle>& aHandle,
311 const RefPtr<SourceMediaStream>& aStream, TrackID aTrackID,
312 StreamTime aDesiredTime, const PrincipalHandle& aPrincipalHandle) {
313 // AppendFrame takes ownership of `segment`
314 VideoSegment segment;
315
316 RefPtr<layers::Image> image;
317 {
318 MutexAutoLock lock(mMutex);
319 // Started - append real image
320 // Stopped - append null
321 // Released - Track is ended, safe to ignore
322 // Can happen because NotifyPull comes from a stream listener
323 if (mState == kReleased) {
324 return;
325 }
326 MOZ_ASSERT(mState != kAllocated);
327 if (mState == kStarted) {
328 MOZ_ASSERT(mStream == aStream);
329 MOZ_ASSERT(mTrackID == aTrackID);
330 image = mImage;
331 }
332 }
333
334 StreamTime delta = aDesiredTime - aStream->GetEndOfAppendedData(aTrackID);
335 if (delta > 0) {
336 // nullptr images are allowed
337 IntSize size(mOpts.mWidth, mOpts.mHeight);
338 segment.AppendFrame(image.forget(), delta, size, aPrincipalHandle);
339 // This can fail if either a) we haven't added the track yet, or b)
340 // we've removed or finished the track.
341 aStream->AppendToTrack(aTrackID, &segment);
342 }
343 }
344
345 /**
346 * Default audio source.
347 */
348
MediaEngineDefaultAudioSource()349 MediaEngineDefaultAudioSource::MediaEngineDefaultAudioSource()
350 : mMutex("MediaEngineDefaultAudioSource::mMutex") {}
351
~MediaEngineDefaultAudioSource()352 MediaEngineDefaultAudioSource::~MediaEngineDefaultAudioSource() {}
353
GetName() const354 nsString MediaEngineDefaultAudioSource::GetName() const {
355 return NS_LITERAL_STRING(u"Default Audio Device");
356 }
357
GetUUID() const358 nsCString MediaEngineDefaultAudioSource::GetUUID() const {
359 return NS_LITERAL_CSTRING("B7CBD7C1-53EF-42F9-8353-73F61C70C092");
360 }
361
GetBestFitnessDistance(const nsTArray<const NormalizedConstraintSet * > & aConstraintSets,const nsString & aDeviceId) const362 uint32_t MediaEngineDefaultAudioSource::GetBestFitnessDistance(
363 const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
364 const nsString& aDeviceId) const {
365 uint32_t distance = 0;
366 #ifdef MOZ_WEBRTC
367 for (const auto* cs : aConstraintSets) {
368 distance =
369 MediaConstraintsHelper::GetMinimumFitnessDistance(*cs, aDeviceId);
370 break; // distance is read from first entry only
371 }
372 #endif
373 return distance;
374 }
375
IsAvailable() const376 bool MediaEngineDefaultAudioSource::IsAvailable() const {
377 AssertIsOnOwningThread();
378
379 return mState == kReleased;
380 }
381
Allocate(const dom::MediaTrackConstraints & aConstraints,const MediaEnginePrefs & aPrefs,const nsString & aDeviceId,const mozilla::ipc::PrincipalInfo & aPrincipalInfo,AllocationHandle ** aOutHandle,const char ** aOutBadConstraint)382 nsresult MediaEngineDefaultAudioSource::Allocate(
383 const dom::MediaTrackConstraints& aConstraints,
384 const MediaEnginePrefs& aPrefs, const nsString& aDeviceId,
385 const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
386 AllocationHandle** aOutHandle, const char** aOutBadConstraint) {
387 AssertIsOnOwningThread();
388
389 MOZ_ASSERT(mState == kReleased);
390
391 // Mock failure for automated tests.
392 if (aConstraints.mDeviceId.IsString() &&
393 aConstraints.mDeviceId.GetAsString().EqualsASCII("bad device")) {
394 return NS_ERROR_FAILURE;
395 }
396
397 mFreq = aPrefs.mFreq ? aPrefs.mFreq : 1000;
398 *aOutHandle = nullptr;
399
400 MutexAutoLock lock(mMutex);
401 mState = kAllocated;
402 return NS_OK;
403 }
404
Deallocate(const RefPtr<const AllocationHandle> & aHandle)405 nsresult MediaEngineDefaultAudioSource::Deallocate(
406 const RefPtr<const AllocationHandle>& aHandle) {
407 AssertIsOnOwningThread();
408
409 MOZ_ASSERT(!aHandle);
410 MOZ_ASSERT(mState == kStopped || mState == kAllocated);
411
412 MutexAutoLock lock(mMutex);
413 if (mStream && IsTrackIDExplicit(mTrackID)) {
414 mStream->EndTrack(mTrackID);
415 mStream = nullptr;
416 mTrackID = TRACK_NONE;
417 }
418 mState = kReleased;
419 return NS_OK;
420 }
421
SetTrack(const RefPtr<const AllocationHandle> & aHandle,const RefPtr<SourceMediaStream> & aStream,TrackID aTrackID,const PrincipalHandle & aPrincipal)422 nsresult MediaEngineDefaultAudioSource::SetTrack(
423 const RefPtr<const AllocationHandle>& aHandle,
424 const RefPtr<SourceMediaStream>& aStream, TrackID aTrackID,
425 const PrincipalHandle& aPrincipal) {
426 AssertIsOnOwningThread();
427
428 MOZ_ASSERT(mState == kAllocated);
429 MOZ_ASSERT(!mStream);
430 MOZ_ASSERT(mTrackID == TRACK_NONE);
431
432 // AddAudioTrack will take ownership of segment
433 mStream = aStream;
434 mTrackID = aTrackID;
435 aStream->AddAudioTrack(aTrackID, aStream->GraphRate(), 0, new AudioSegment(),
436 SourceMediaStream::ADDTRACK_QUEUED);
437 return NS_OK;
438 }
439
Start(const RefPtr<const AllocationHandle> & aHandle)440 nsresult MediaEngineDefaultAudioSource::Start(
441 const RefPtr<const AllocationHandle>& aHandle) {
442 AssertIsOnOwningThread();
443
444 MOZ_ASSERT(mState == kAllocated || mState == kStopped);
445 MOZ_ASSERT(mStream, "SetTrack() must happen before Start()");
446 MOZ_ASSERT(IsTrackIDExplicit(mTrackID),
447 "SetTrack() must happen before Start()");
448
449 if (!mSineGenerator) {
450 // generate sine wave (default 1KHz)
451 mSineGenerator = new SineWaveGenerator(mStream->GraphRate(), mFreq);
452 }
453
454 MutexAutoLock lock(mMutex);
455 mState = kStarted;
456 return NS_OK;
457 }
458
Stop(const RefPtr<const AllocationHandle> & aHandle)459 nsresult MediaEngineDefaultAudioSource::Stop(
460 const RefPtr<const AllocationHandle>& aHandle) {
461 AssertIsOnOwningThread();
462
463 if (mState == kStopped || mState == kAllocated) {
464 return NS_OK;
465 }
466
467 MOZ_ASSERT(mState == kStarted);
468
469 MutexAutoLock lock(mMutex);
470 mState = kStopped;
471 return NS_OK;
472 }
473
Reconfigure(const RefPtr<AllocationHandle> & aHandle,const dom::MediaTrackConstraints & aConstraints,const MediaEnginePrefs & aPrefs,const nsString & aDeviceId,const char ** aOutBadConstraint)474 nsresult MediaEngineDefaultAudioSource::Reconfigure(
475 const RefPtr<AllocationHandle>& aHandle,
476 const dom::MediaTrackConstraints& aConstraints,
477 const MediaEnginePrefs& aPrefs, const nsString& aDeviceId,
478 const char** aOutBadConstraint) {
479 return NS_OK;
480 }
481
AppendToSegment(AudioSegment & aSegment,TrackTicks aSamples,const PrincipalHandle & aPrincipalHandle)482 void MediaEngineDefaultAudioSource::AppendToSegment(
483 AudioSegment& aSegment, TrackTicks aSamples,
484 const PrincipalHandle& aPrincipalHandle) {
485 RefPtr<SharedBuffer> buffer =
486 SharedBuffer::Create(aSamples * sizeof(int16_t));
487 int16_t* dest = static_cast<int16_t*>(buffer->Data());
488
489 mSineGenerator->generate(dest, aSamples);
490 AutoTArray<const int16_t*, 1> channels;
491 channels.AppendElement(dest);
492 aSegment.AppendFrames(buffer.forget(), channels, aSamples, aPrincipalHandle);
493 }
494
Pull(const RefPtr<const AllocationHandle> & aHandle,const RefPtr<SourceMediaStream> & aStream,TrackID aTrackID,StreamTime aDesiredTime,const PrincipalHandle & aPrincipalHandle)495 void MediaEngineDefaultAudioSource::Pull(
496 const RefPtr<const AllocationHandle>& aHandle,
497 const RefPtr<SourceMediaStream>& aStream, TrackID aTrackID,
498 StreamTime aDesiredTime, const PrincipalHandle& aPrincipalHandle) {
499 AudioSegment segment;
500 // avoid accumulating rounding errors
501 TrackTicks desired =
502 aStream->TimeToTicksRoundUp(aStream->GraphRate(), aDesiredTime);
503 TrackTicks delta = desired - mLastNotify;
504 mLastNotify += delta;
505 AppendToSegment(segment, delta, aPrincipalHandle);
506 aStream->AppendToTrack(aTrackID, &segment);
507 }
508
EnumerateDevices(uint64_t aWindowId,dom::MediaSourceEnum aMediaSource,nsTArray<RefPtr<MediaEngineSource>> * aSources)509 void MediaEngineDefault::EnumerateDevices(
510 uint64_t aWindowId, dom::MediaSourceEnum aMediaSource,
511 nsTArray<RefPtr<MediaEngineSource>>* aSources) {
512 AssertIsOnOwningThread();
513
514 switch (aMediaSource) {
515 case dom::MediaSourceEnum::Camera: {
516 // Only supports camera video sources. See Bug 1038241.
517
518 // We once had code here to find a VideoSource with the same settings and
519 // re-use that. This is no longer possible since the resolution gets set
520 // in Allocate().
521
522 nsTArray<RefPtr<MediaEngineSource>>* devicesForThisWindow =
523 mVSources.LookupOrAdd(aWindowId);
524 auto newSource = MakeRefPtr<MediaEngineDefaultVideoSource>();
525 devicesForThisWindow->AppendElement(newSource);
526 aSources->AppendElement(newSource);
527 return;
528 }
529 case dom::MediaSourceEnum::Microphone: {
530 nsTArray<RefPtr<MediaEngineDefaultAudioSource>>* devicesForThisWindow =
531 mASources.LookupOrAdd(aWindowId);
532 for (const RefPtr<MediaEngineDefaultAudioSource>& source :
533 *devicesForThisWindow) {
534 if (source->IsAvailable()) {
535 aSources->AppendElement(source);
536 }
537 }
538
539 if (aSources->IsEmpty()) {
540 // All streams are currently busy, just make a new one.
541 auto newSource = MakeRefPtr<MediaEngineDefaultAudioSource>();
542 devicesForThisWindow->AppendElement(newSource);
543 aSources->AppendElement(newSource);
544 }
545 return;
546 }
547 default:
548 MOZ_ASSERT_UNREACHABLE("Unsupported source type");
549 return;
550 }
551 }
552
ReleaseResourcesForWindow(uint64_t aWindowId)553 void MediaEngineDefault::ReleaseResourcesForWindow(uint64_t aWindowId) {
554 nsTArray<RefPtr<MediaEngineDefaultAudioSource>>* audioDevicesForThisWindow =
555 mASources.Get(aWindowId);
556
557 if (audioDevicesForThisWindow) {
558 for (const RefPtr<MediaEngineDefaultAudioSource>& source :
559 *audioDevicesForThisWindow) {
560 source->Shutdown();
561 }
562 }
563
564 mASources.Remove(aWindowId);
565
566 nsTArray<RefPtr<MediaEngineSource>>* videoDevicesForThisWindow =
567 mVSources.Get(aWindowId);
568
569 if (videoDevicesForThisWindow) {
570 for (const RefPtr<MediaEngineSource>& source : *videoDevicesForThisWindow) {
571 source->Shutdown();
572 }
573 }
574
575 mVSources.Remove(aWindowId);
576 }
577
Shutdown()578 void MediaEngineDefault::Shutdown() {
579 AssertIsOnOwningThread();
580
581 for (auto iter = mVSources.Iter(); !iter.Done(); iter.Next()) {
582 for (const RefPtr<MediaEngineSource>& source : *iter.UserData()) {
583 if (source) {
584 source->Shutdown();
585 }
586 }
587 }
588 for (auto iter = mASources.Iter(); !iter.Done(); iter.Next()) {
589 for (const RefPtr<MediaEngineDefaultAudioSource>& source :
590 *iter.UserData()) {
591 if (source) {
592 source->Shutdown();
593 }
594 }
595 }
596 mVSources.Clear();
597 mASources.Clear();
598 };
599
600 } // namespace mozilla
601