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*>(&timestamp),
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