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/. */
5 #ifndef MOZILLA_MEDIAMANAGER_H
6 #define MOZILLA_MEDIAMANAGER_H
8 #include "MediaEnginePrefs.h"
9 #include "MediaEventSource.h"
10 #include "mozilla/dom/GetUserMediaRequest.h"
11 #include "mozilla/Unused.h"
12 #include "nsIMediaDevice.h"
13 #include "nsIMediaManager.h"
15 #include "nsHashKeys.h"
16 #include "nsClassHashtable.h"
17 #include "nsRefPtrHashtable.h"
18 #include "nsIMemoryReporter.h"
19 #include "nsIObserver.h"
21 #include "nsXULAppAPI.h"
22 #include "mozilla/Attributes.h"
23 #include "mozilla/Preferences.h"
24 #include "mozilla/StaticPtr.h"
25 #include "mozilla/dom/MediaStreamBinding.h"
26 #include "mozilla/dom/MediaStreamTrackBinding.h"
27 #include "mozilla/dom/MediaStreamError.h"
28 #include "mozilla/dom/NavigatorBinding.h"
29 #include "mozilla/media/MediaChild.h"
30 #include "mozilla/media/MediaParent.h"
31 #include "mozilla/Logging.h"
32 #include "mozilla/UniquePtr.h"
33 #include "DOMMediaStream.h"
34 #include "PerformanceRecorder.h"
37 # include "transport/runnable_utils.h"
40 class AudioDeviceInfo
;
44 class WebrtcLogSinkHandle
;
49 class MediaEngineSource
;
54 struct AudioOutputOptions
;
55 struct MediaStreamConstraints
;
56 struct MediaTrackConstraints
;
57 struct MediaTrackConstraintSet
;
58 struct MediaTrackSettings
;
59 enum class CallerType
: uint32_t;
60 enum class MediaDeviceKind
: uint8_t;
67 class GetUserMediaTask
;
68 class GetUserMediaWindowListener
;
73 * Device info that is independent of any Window.
74 * MediaDevices can be shared, unlike LocalMediaDevices.
76 class MediaDevice final
{
78 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDevice
)
81 * Whether source device does end-run around cross origin restrictions.
83 enum class IsScary
{ No
, Yes
};
86 * Whether source device can use OS level selection prompt
88 enum class OsPromptable
{ No
, Yes
};
91 * Whether source device is just a placeholder
93 enum class IsPlaceholder
{ No
, Yes
};
95 MediaDevice(MediaEngine
* aEngine
, dom::MediaSourceEnum aMediaSource
,
96 const nsString
& aRawName
, const nsString
& aRawID
,
97 const nsString
& aRawGroupID
, IsScary aIsScary
,
98 const OsPromptable canRequestOsLevelPrompt
,
99 const IsPlaceholder aIsPlaceholder
= IsPlaceholder::No
);
101 MediaDevice(MediaEngine
* aEngine
,
102 const RefPtr
<AudioDeviceInfo
>& aAudioDeviceInfo
,
103 const nsString
& aRawID
);
105 static RefPtr
<MediaDevice
> CopyWithNewRawGroupId(
106 const RefPtr
<MediaDevice
>& aOther
, const nsString
& aRawGroupID
);
108 dom::MediaSourceEnum
GetMediaSource() const;
114 const RefPtr
<MediaEngine
> mEngine
;
115 const RefPtr
<AudioDeviceInfo
> mAudioDeviceInfo
;
116 const dom::MediaSourceEnum mMediaSource
;
117 const dom::MediaDeviceKind mKind
;
119 const bool mCanRequestOsLevelPrompt
;
121 const bool mIsPlaceholder
;
122 const nsString mType
;
123 const nsString mRawID
;
124 const nsString mRawGroupID
;
125 const nsString mRawName
;
129 * Device info that is specific to a particular Window. If the device is a
130 * source device, then a single corresponding MediaEngineSource is provided,
131 * which can provide a maximum of one capture stream. LocalMediaDevices are
132 * not shared, but APIs returning LocalMediaDevices return a new object each
135 class LocalMediaDevice final
: public nsIMediaDevice
{
137 NS_DECL_THREADSAFE_ISUPPORTS
138 NS_DECL_NSIMEDIADEVICE
140 LocalMediaDevice(RefPtr
<const MediaDevice
> aRawDevice
, const nsString
& aID
,
141 const nsString
& aGroupID
, const nsString
& aName
);
143 uint32_t GetBestFitnessDistance(
144 const nsTArray
<const NormalizedConstraintSet
*>& aConstraintSets
,
145 dom::CallerType aCallerType
);
147 nsresult
Allocate(const dom::MediaTrackConstraints
& aConstraints
,
148 const MediaEnginePrefs
& aPrefs
, uint64_t aWindowId
,
149 const char** aOutBadConstraint
);
150 void SetTrack(const RefPtr
<MediaTrack
>& aTrack
,
151 const nsMainThreadPtrHandle
<nsIPrincipal
>& aPrincipal
);
153 nsresult
Reconfigure(const dom::MediaTrackConstraints
& aConstraints
,
154 const MediaEnginePrefs
& aPrefs
,
155 const char** aOutBadConstraint
);
156 nsresult
FocusOnSelectedSource();
158 nsresult
Deallocate();
160 void GetSettings(dom::MediaTrackSettings
& aOutSettings
);
161 MediaEngineSource
* Source();
162 const TrackingId
& GetTrackingId() const;
163 // Returns null if not a physical audio device.
164 AudioDeviceInfo
* GetAudioDeviceInfo() const {
165 return mRawDevice
->mAudioDeviceInfo
;
167 dom::MediaSourceEnum
GetMediaSource() const {
168 return mRawDevice
->GetMediaSource();
170 dom::MediaDeviceKind
Kind() const { return mRawDevice
->mKind
; }
171 bool IsFake() const { return mRawDevice
->mIsFake
; }
172 const nsString
& RawID() { return mRawDevice
->mRawID
; }
175 virtual ~LocalMediaDevice() = default;
177 static uint32_t FitnessDistance(
179 const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters
&
182 static bool StringsContain(const dom::OwningStringOrStringSequence
& aStrings
,
184 static uint32_t FitnessDistance(
185 nsString aN
, const dom::ConstrainDOMStringParameters
& aParams
);
188 const RefPtr
<const MediaDevice
> mRawDevice
;
189 const nsString mName
;
191 const nsString mGroupID
;
194 RefPtr
<MediaEngineSource
> mSource
;
197 typedef nsRefPtrHashtable
<nsUint64HashKey
, GetUserMediaWindowListener
>
200 class MediaManager final
: public nsIMediaManagerService
,
201 public nsIMemoryReporter
,
203 friend DeviceListener
;
206 static already_AddRefed
<MediaManager
> GetInstance();
208 // NOTE: never NS_DispatchAndSpinEventLoopUntilComplete to the MediaManager
209 // thread from the MainThread, as we NS_DispatchAndSpinEventLoopUntilComplete
210 // to MainThread from MediaManager thread.
211 static MediaManager
* Get();
212 static MediaManager
* GetIfExists();
213 static void Dispatch(already_AddRefed
<Runnable
> task
);
216 * Posts an async operation to the media manager thread.
217 * FunctionType must be a function that takes a `MozPromiseHolder&`.
219 * The returned promise is resolved or rejected by aFunction on the media
222 template <typename MozPromiseType
, typename FunctionType
>
223 static RefPtr
<MozPromiseType
> Dispatch(const char* aName
,
224 FunctionType
&& aFunction
);
227 static bool IsInMediaThread();
230 static bool Exists() { return !!GetIfExists(); }
232 static nsresult
NotifyRecordingStatusChange(nsPIDOMWindowInner
* aWindow
);
234 NS_DECL_THREADSAFE_ISUPPORTS
236 NS_DECL_NSIMEMORYREPORTER
237 NS_DECL_NSIMEDIAMANAGERSERVICE
239 media::Parent
<media::NonE10s
>* GetNonE10sParent();
241 // If the window has not been destroyed, then return the
242 // GetUserMediaWindowListener for this window.
243 // If the window has been destroyed, then return null.
244 RefPtr
<GetUserMediaWindowListener
> GetOrMakeWindowListener(
245 nsPIDOMWindowInner
* aWindow
);
246 WindowTable
* GetActiveWindows() {
247 MOZ_ASSERT(NS_IsMainThread());
248 return &mActiveWindows
;
250 GetUserMediaWindowListener
* GetWindowListener(uint64_t aWindowId
) {
251 MOZ_ASSERT(NS_IsMainThread());
252 return mActiveWindows
.GetWeak(aWindowId
);
254 void AddWindowID(uint64_t aWindowId
,
255 RefPtr
<GetUserMediaWindowListener
> aListener
);
256 void RemoveWindowID(uint64_t aWindowId
);
257 void SendPendingGUMRequest();
258 bool IsWindowStillActive(uint64_t aWindowId
) {
259 return !!GetWindowListener(aWindowId
);
261 bool IsWindowListenerStillActive(
262 const RefPtr
<GetUserMediaWindowListener
>& aListener
);
264 static bool IsOn(const dom::OwningBooleanOrMediaTrackConstraints
& aUnion
) {
265 return !aUnion
.IsBoolean() || aUnion
.GetAsBoolean();
267 using GetUserMediaSuccessCallback
= dom::NavigatorUserMediaSuccessCallback
;
268 using GetUserMediaErrorCallback
= dom::NavigatorUserMediaErrorCallback
;
271 static void CallOnError(GetUserMediaErrorCallback
& aCallback
,
272 dom::MediaStreamError
& aError
);
274 static void CallOnSuccess(GetUserMediaSuccessCallback
& aCallback
,
275 DOMMediaStream
& aTrack
);
277 using MediaDeviceSet
= nsTArray
<RefPtr
<MediaDevice
>>;
278 using MediaDeviceSetRefCnt
= media::Refcountable
<MediaDeviceSet
>;
279 using LocalMediaDeviceSet
= nsTArray
<RefPtr
<LocalMediaDevice
>>;
280 using LocalMediaDeviceSetRefCnt
= media::Refcountable
<LocalMediaDeviceSet
>;
282 using StreamPromise
=
283 MozPromise
<RefPtr
<DOMMediaStream
>, RefPtr
<MediaMgrError
>, true>;
284 using DeviceSetPromise
=
285 MozPromise
<RefPtr
<MediaDeviceSetRefCnt
>, RefPtr
<MediaMgrError
>, true>;
286 using ConstDeviceSetPromise
= MozPromise
<RefPtr
<const MediaDeviceSetRefCnt
>,
287 RefPtr
<MediaMgrError
>, true>;
288 using LocalDevicePromise
=
289 MozPromise
<RefPtr
<LocalMediaDevice
>, RefPtr
<MediaMgrError
>, true>;
290 using LocalDeviceSetPromise
= MozPromise
<RefPtr
<LocalMediaDeviceSetRefCnt
>,
291 RefPtr
<MediaMgrError
>, true>;
292 using MgrPromise
= MozPromise
<bool, RefPtr
<MediaMgrError
>, true>;
294 RefPtr
<StreamPromise
> GetUserMedia(
295 nsPIDOMWindowInner
* aWindow
,
296 const dom::MediaStreamConstraints
& aConstraints
,
297 dom::CallerType aCallerType
);
299 RefPtr
<LocalDevicePromise
> SelectAudioOutput(
300 nsPIDOMWindowInner
* aWindow
, const dom::AudioOutputOptions
& aOptions
,
301 dom::CallerType aCallerType
);
303 // Return the list of microphone, camera, and speaker devices.
304 // MediaDeviceSets provided on promise resolution are shared between
305 // callers and so cannot be modified.
306 RefPtr
<ConstDeviceSetPromise
> GetPhysicalDevices();
308 void OnNavigation(uint64_t aWindowID
);
309 void OnCameraMute(bool aMute
);
310 void OnMicrophoneMute(bool aMute
);
311 bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId
);
313 MediaEventSource
<void>& DeviceListChangeEvent() {
314 return mDeviceListChangeEvent
;
316 RefPtr
<LocalDeviceSetPromise
> AnonymizeDevices(
317 nsPIDOMWindowInner
* aWindow
, RefPtr
<const MediaDeviceSetRefCnt
> aDevices
);
319 MediaEnginePrefs mPrefs
;
322 static nsresult
GenerateUUID(nsAString
& aResult
);
326 * This function tries to guess the group id for a video device in aDevices
327 * based on the device name. If the name of only one audio device in aAudios
328 * contains the name of the video device, then, this video device will take
329 * the group id of the audio device. Since this is a guess we try to minimize
330 * the probability of false positive. If we fail to find a correlation we
331 * leave the video group id untouched. In that case the group id will be the
334 static void GuessVideoDeviceGroupIDs(MediaDeviceSet
& aDevices
,
335 const MediaDeviceSet
& aAudios
);
338 enum class EnumerationFlag
{
339 AllowPermissionRequest
,
340 EnumerateAudioOutputs
,
343 using EnumerationFlags
= EnumSet
<EnumerationFlag
>;
345 enum class DeviceType
{ Real
, Fake
};
347 struct DeviceEnumerationParams
{
348 DeviceEnumerationParams(dom::MediaSourceEnum aInputType
, DeviceType aType
,
349 nsAutoCString aForcedDeviceName
);
350 dom::MediaSourceEnum mInputType
;
352 nsAutoCString mForcedDeviceName
;
355 struct VideoDeviceEnumerationParams
: public DeviceEnumerationParams
{
356 VideoDeviceEnumerationParams(dom::MediaSourceEnum aInputType
,
358 nsAutoCString aForcedDeviceName
,
359 nsAutoCString aForcedMicrophoneName
);
361 // The by-pref forced microphone device name, used for groupId correlation
362 // of camera devices.
363 nsAutoCString mForcedMicrophoneName
;
366 struct EnumerationParams
{
367 EnumerationParams(EnumerationFlags aFlags
,
368 Maybe
<VideoDeviceEnumerationParams
> aVideo
,
369 Maybe
<DeviceEnumerationParams
> aAudio
);
370 bool HasFakeCams() const;
371 bool HasFakeMics() const;
372 bool RealDeviceRequested() const;
373 dom::MediaSourceEnum
VideoInputType() const;
374 dom::MediaSourceEnum
AudioInputType() const;
375 EnumerationFlags mFlags
;
376 Maybe
<VideoDeviceEnumerationParams
> mVideo
;
377 Maybe
<DeviceEnumerationParams
> mAudio
;
380 static EnumerationParams
CreateEnumerationParams(
381 dom::MediaSourceEnum aVideoInputType
,
382 dom::MediaSourceEnum aAudioInputType
, EnumerationFlags aFlags
);
384 RefPtr
<LocalDeviceSetPromise
> EnumerateDevicesImpl(
385 nsPIDOMWindowInner
* aWindow
, EnumerationParams aParams
);
387 RefPtr
<DeviceSetPromise
> MaybeRequestPermissionAndEnumerateRawDevices(
388 EnumerationParams aParams
);
390 static RefPtr
<MediaDeviceSetRefCnt
> EnumerateRawDevices(
391 EnumerationParams aParams
);
393 RefPtr
<LocalDeviceSetPromise
> SelectSettings(
394 const dom::MediaStreamConstraints
& aConstraints
,
395 dom::CallerType aCallerType
, RefPtr
<LocalMediaDeviceSetRefCnt
> aDevices
);
397 void GetPref(nsIPrefBranch
* aBranch
, const char* aPref
, const char* aData
,
399 void GetPrefBool(nsIPrefBranch
* aBranch
, const char* aPref
, const char* aData
,
401 void GetPrefs(nsIPrefBranch
* aBranch
, const char* aData
);
403 // Make private because we want only one instance of this class
404 explicit MediaManager(already_AddRefed
<TaskQueue
> aMediaThread
);
406 ~MediaManager() = default;
409 void StopScreensharing(uint64_t aWindowID
);
411 void RemoveMediaDevicesCallback(uint64_t aWindowID
);
412 void DeviceListChanged();
413 void EnsureNoPlaceholdersInDeviceCache();
414 void InvalidateDeviceCache();
415 void HandleDeviceListChanged();
417 // Returns the number of incomplete tasks associated with this window,
418 // including the newly added task.
419 size_t AddTaskAndGetCount(uint64_t aWindowID
, const nsAString
& aCallID
,
420 RefPtr
<GetUserMediaTask
> aTask
);
421 // Finds the task corresponding to aCallID and removes it from tracking.
422 RefPtr
<GetUserMediaTask
> TakeGetUserMediaTask(const nsAString
& aCallID
);
423 // Intended for use with "media.navigator.permission.disabled" to bypass the
424 // permission prompt and use the first appropriate device.
425 void NotifyAllowed(const nsString
& aCallID
,
426 const LocalMediaDeviceSet
& aDevices
);
429 MediaEngine
* GetBackend();
431 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf
);
433 // ONLY access from MainThread so we don't need to lock
434 WindowTable mActiveWindows
;
435 nsRefPtrHashtable
<nsStringHashKey
, GetUserMediaTask
> mActiveCallbacks
;
436 nsClassHashtable
<nsUint64HashKey
, nsTArray
<nsString
>> mCallIds
;
437 nsTArray
<RefPtr
<dom::GetUserMediaRequest
>> mPendingGUMRequest
;
439 RefPtr
<WebrtcLogSinkHandle
> mLogHandle
;
441 // non-null if a device enumeration is in progress and was started after the
442 // last device-change invalidation
443 RefPtr
<media::Refcountable
<nsTArray
<MozPromiseHolder
<ConstDeviceSetPromise
>>>>
444 mPendingDevicesPromises
;
445 RefPtr
<MediaDeviceSetRefCnt
> mPhysicalDevices
;
446 TimeStamp mUnhandledDeviceChangeTime
;
447 RefPtr
<MediaTimer
> mDeviceChangeTimer
;
448 bool mCamerasMuted
= false;
449 bool mMicrophonesMuted
= false;
453 const RefPtr
<TaskQueue
> mMediaThread
;
456 nsCOMPtr
<nsIAsyncShutdownBlocker
> mShutdownBlocker
;
458 // ONLY accessed from MediaManagerThread
459 RefPtr
<MediaEngine
> mBackend
;
461 // Accessed only on main thread and mMediaThread.
462 // Set before mMediaThread is created, and cleared on main thread after last
463 // mMediaThread task is run.
464 static StaticRefPtr
<MediaManager
> sSingleton
;
466 // Connect/Disconnect on media thread only
467 MediaEventListener mDeviceListChangeListener
;
469 MediaEventProducer
<void> mDeviceListChangeEvent
;
472 RefPtr
<media::Parent
<media::NonE10s
>> mNonE10sParent
;
475 } // namespace mozilla
477 #endif // MOZILLA_MEDIAMANAGER_H