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
;
45 class MediaEngineSource
;
50 struct AudioOutputOptions
;
51 struct MediaStreamConstraints
;
52 struct MediaTrackConstraints
;
53 struct MediaTrackConstraintSet
;
54 struct MediaTrackSettings
;
55 enum class CallerType
: uint32_t;
56 enum class MediaDeviceKind
: uint8_t;
63 class GetUserMediaTask
;
64 class GetUserMediaWindowListener
;
69 * Device info that is independent of any Window.
70 * MediaDevices can be shared, unlike LocalMediaDevices.
72 class MediaDevice final
{
74 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDevice
)
77 * Whether source device does end-run around cross origin restrictions.
79 enum class IsScary
{ No
, Yes
};
82 * Whether source device can use OS level selection prompt
84 enum class OsPromptable
{ No
, Yes
};
86 MediaDevice(MediaEngine
* aEngine
, dom::MediaSourceEnum aMediaSource
,
87 const nsString
& aRawName
, const nsString
& aRawID
,
88 const nsString
& aRawGroupID
, IsScary aIsScary
,
89 const OsPromptable canRequestOsLevelPrompt
);
91 MediaDevice(MediaEngine
* aEngine
,
92 const RefPtr
<AudioDeviceInfo
>& aAudioDeviceInfo
,
93 const nsString
& aRawID
);
95 static RefPtr
<MediaDevice
> CopyWithNewRawGroupId(
96 const RefPtr
<MediaDevice
>& aOther
, const nsString
& aRawGroupID
);
98 dom::MediaSourceEnum
GetMediaSource() const;
104 const RefPtr
<MediaEngine
> mEngine
;
105 const RefPtr
<AudioDeviceInfo
> mAudioDeviceInfo
;
106 const dom::MediaSourceEnum mMediaSource
;
107 const dom::MediaDeviceKind mKind
;
109 const bool mCanRequestOsLevelPrompt
;
111 const nsString mType
;
112 const nsString mRawID
;
113 const nsString mRawGroupID
;
114 const nsString mRawName
;
118 * Device info that is specific to a particular Window. If the device is a
119 * source device, then a single corresponding MediaEngineSource is provided,
120 * which can provide a maximum of one capture stream. LocalMediaDevices are
121 * not shared, but APIs returning LocalMediaDevices return a new object each
124 class LocalMediaDevice final
: public nsIMediaDevice
{
126 NS_DECL_THREADSAFE_ISUPPORTS
127 NS_DECL_NSIMEDIADEVICE
129 LocalMediaDevice(RefPtr
<const MediaDevice
> aRawDevice
, const nsString
& aID
,
130 const nsString
& aGroupID
, const nsString
& aName
);
132 uint32_t GetBestFitnessDistance(
133 const nsTArray
<const NormalizedConstraintSet
*>& aConstraintSets
,
134 dom::CallerType aCallerType
);
136 nsresult
Allocate(const dom::MediaTrackConstraints
& aConstraints
,
137 const MediaEnginePrefs
& aPrefs
, uint64_t aWindowId
,
138 const char** aOutBadConstraint
);
139 void SetTrack(const RefPtr
<MediaTrack
>& aTrack
,
140 const nsMainThreadPtrHandle
<nsIPrincipal
>& aPrincipal
);
142 nsresult
Reconfigure(const dom::MediaTrackConstraints
& aConstraints
,
143 const MediaEnginePrefs
& aPrefs
,
144 const char** aOutBadConstraint
);
145 nsresult
FocusOnSelectedSource();
147 nsresult
Deallocate();
149 void GetSettings(dom::MediaTrackSettings
& aOutSettings
);
150 MediaEngineSource
* Source();
151 const TrackingId
& GetTrackingId() const;
152 // Returns null if not a physical audio device.
153 AudioDeviceInfo
* GetAudioDeviceInfo() const {
154 return mRawDevice
->mAudioDeviceInfo
;
156 dom::MediaSourceEnum
GetMediaSource() const {
157 return mRawDevice
->GetMediaSource();
159 dom::MediaDeviceKind
Kind() const { return mRawDevice
->mKind
; }
160 bool IsFake() const { return mRawDevice
->mIsFake
; }
161 const nsString
& RawID() { return mRawDevice
->mRawID
; }
164 virtual ~LocalMediaDevice() = default;
166 static uint32_t FitnessDistance(
168 const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters
&
171 static bool StringsContain(const dom::OwningStringOrStringSequence
& aStrings
,
173 static uint32_t FitnessDistance(
174 nsString aN
, const dom::ConstrainDOMStringParameters
& aParams
);
177 const RefPtr
<const MediaDevice
> mRawDevice
;
178 const nsString mName
;
180 const nsString mGroupID
;
183 RefPtr
<MediaEngineSource
> mSource
;
186 typedef nsRefPtrHashtable
<nsUint64HashKey
, GetUserMediaWindowListener
>
189 class MediaManager final
: public nsIMediaManagerService
,
190 public nsIMemoryReporter
,
192 friend DeviceListener
;
195 static already_AddRefed
<MediaManager
> GetInstance();
197 // NOTE: never NS_DispatchAndSpinEventLoopUntilComplete to the MediaManager
198 // thread from the MainThread, as we NS_DispatchAndSpinEventLoopUntilComplete
199 // to MainThread from MediaManager thread.
200 static MediaManager
* Get();
201 static MediaManager
* GetIfExists();
202 static void StartupInit();
203 static void Dispatch(already_AddRefed
<Runnable
> task
);
206 * Posts an async operation to the media manager thread.
207 * FunctionType must be a function that takes a `MozPromiseHolder&`.
209 * The returned promise is resolved or rejected by aFunction on the media
212 template <typename MozPromiseType
, typename FunctionType
>
213 static RefPtr
<MozPromiseType
> Dispatch(const char* aName
,
214 FunctionType
&& aFunction
);
217 static bool IsInMediaThread();
220 static bool Exists() { return !!GetIfExists(); }
222 static nsresult
NotifyRecordingStatusChange(nsPIDOMWindowInner
* aWindow
);
224 NS_DECL_THREADSAFE_ISUPPORTS
226 NS_DECL_NSIMEMORYREPORTER
227 NS_DECL_NSIMEDIAMANAGERSERVICE
229 media::Parent
<media::NonE10s
>* GetNonE10sParent();
231 // If the window has not been destroyed, then return the
232 // GetUserMediaWindowListener for this window.
233 // If the window has been destroyed, then return null.
234 RefPtr
<GetUserMediaWindowListener
> GetOrMakeWindowListener(
235 nsPIDOMWindowInner
* aWindow
);
236 WindowTable
* GetActiveWindows() {
237 MOZ_ASSERT(NS_IsMainThread());
238 return &mActiveWindows
;
240 GetUserMediaWindowListener
* GetWindowListener(uint64_t aWindowId
) {
241 MOZ_ASSERT(NS_IsMainThread());
242 return mActiveWindows
.GetWeak(aWindowId
);
244 void AddWindowID(uint64_t aWindowId
,
245 RefPtr
<GetUserMediaWindowListener
> aListener
);
246 void RemoveWindowID(uint64_t aWindowId
);
247 void SendPendingGUMRequest();
248 bool IsWindowStillActive(uint64_t aWindowId
) {
249 return !!GetWindowListener(aWindowId
);
251 bool IsWindowListenerStillActive(
252 const RefPtr
<GetUserMediaWindowListener
>& aListener
);
254 static bool IsOn(const dom::OwningBooleanOrMediaTrackConstraints
& aUnion
) {
255 return !aUnion
.IsBoolean() || aUnion
.GetAsBoolean();
257 using GetUserMediaSuccessCallback
= dom::NavigatorUserMediaSuccessCallback
;
258 using GetUserMediaErrorCallback
= dom::NavigatorUserMediaErrorCallback
;
261 static void CallOnError(GetUserMediaErrorCallback
& aCallback
,
262 dom::MediaStreamError
& aError
);
264 static void CallOnSuccess(GetUserMediaSuccessCallback
& aCallback
,
265 DOMMediaStream
& aTrack
);
267 using MediaDeviceSet
= nsTArray
<RefPtr
<MediaDevice
>>;
268 using MediaDeviceSetRefCnt
= media::Refcountable
<MediaDeviceSet
>;
269 using LocalMediaDeviceSet
= nsTArray
<RefPtr
<LocalMediaDevice
>>;
270 using LocalMediaDeviceSetRefCnt
= media::Refcountable
<LocalMediaDeviceSet
>;
272 using StreamPromise
=
273 MozPromise
<RefPtr
<DOMMediaStream
>, RefPtr
<MediaMgrError
>, true>;
274 using DeviceSetPromise
=
275 MozPromise
<RefPtr
<MediaDeviceSetRefCnt
>, RefPtr
<MediaMgrError
>, true>;
276 using ConstDeviceSetPromise
= MozPromise
<RefPtr
<const MediaDeviceSetRefCnt
>,
277 RefPtr
<MediaMgrError
>, true>;
278 using LocalDevicePromise
=
279 MozPromise
<RefPtr
<LocalMediaDevice
>, RefPtr
<MediaMgrError
>, true>;
280 using LocalDeviceSetPromise
= MozPromise
<RefPtr
<LocalMediaDeviceSetRefCnt
>,
281 RefPtr
<MediaMgrError
>, true>;
282 using MgrPromise
= MozPromise
<bool, RefPtr
<MediaMgrError
>, true>;
284 RefPtr
<StreamPromise
> GetUserMedia(
285 nsPIDOMWindowInner
* aWindow
,
286 const dom::MediaStreamConstraints
& aConstraints
,
287 dom::CallerType aCallerType
);
289 RefPtr
<LocalDevicePromise
> SelectAudioOutput(
290 nsPIDOMWindowInner
* aWindow
, const dom::AudioOutputOptions
& aOptions
,
291 dom::CallerType aCallerType
);
293 // Return the list of microphone, camera, and speaker devices.
294 // MediaDeviceSets provided on promise resolution are shared between
295 // callers and so cannot be modified.
296 RefPtr
<ConstDeviceSetPromise
> GetPhysicalDevices();
298 void OnNavigation(uint64_t aWindowID
);
299 void OnCameraMute(bool aMute
);
300 void OnMicrophoneMute(bool aMute
);
301 bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId
);
303 MediaEventSource
<void>& DeviceListChangeEvent() {
304 return mDeviceListChangeEvent
;
306 RefPtr
<LocalDeviceSetPromise
> AnonymizeDevices(
307 nsPIDOMWindowInner
* aWindow
, RefPtr
<const MediaDeviceSetRefCnt
> aDevices
);
309 MediaEnginePrefs mPrefs
;
312 static nsresult
GenerateUUID(nsAString
& aResult
);
316 * This function tries to guess the group id for a video device in aDevices
317 * based on the device name. If the name of only one audio device in aAudios
318 * contains the name of the video device, then, this video device will take
319 * the group id of the audio device. Since this is a guess we try to minimize
320 * the probability of false positive. If we fail to find a correlation we
321 * leave the video group id untouched. In that case the group id will be the
324 static void GuessVideoDeviceGroupIDs(MediaDeviceSet
& aDevices
,
325 const MediaDeviceSet
& aAudios
);
328 enum class EnumerationFlag
{
329 AllowPermissionRequest
,
330 EnumerateAudioOutputs
,
333 using EnumerationFlags
= EnumSet
<EnumerationFlag
>;
334 RefPtr
<LocalDeviceSetPromise
> EnumerateDevicesImpl(
335 nsPIDOMWindowInner
* aWindow
, dom::MediaSourceEnum aVideoInputType
,
336 dom::MediaSourceEnum aAudioInputType
, EnumerationFlags aFlags
);
338 RefPtr
<DeviceSetPromise
> EnumerateRawDevices(
339 dom::MediaSourceEnum aVideoInputType
,
340 dom::MediaSourceEnum aAudioInputType
, EnumerationFlags aFlags
);
342 RefPtr
<LocalDeviceSetPromise
> SelectSettings(
343 const dom::MediaStreamConstraints
& aConstraints
,
344 dom::CallerType aCallerType
, RefPtr
<LocalMediaDeviceSetRefCnt
> aDevices
);
346 void GetPref(nsIPrefBranch
* aBranch
, const char* aPref
, const char* aData
,
348 void GetPrefBool(nsIPrefBranch
* aBranch
, const char* aPref
, const char* aData
,
350 void GetPrefs(nsIPrefBranch
* aBranch
, const char* aData
);
352 // Make private because we want only one instance of this class
353 explicit MediaManager(already_AddRefed
<TaskQueue
> aMediaThread
);
355 ~MediaManager() = default;
358 void StopScreensharing(uint64_t aWindowID
);
360 void RemoveMediaDevicesCallback(uint64_t aWindowID
);
361 void DeviceListChanged();
362 void InvalidateDeviceCache();
363 void HandleDeviceListChanged();
365 // Returns the number of incomplete tasks associated with this window,
366 // including the newly added task.
367 size_t AddTaskAndGetCount(uint64_t aWindowID
, const nsAString
& aCallID
,
368 RefPtr
<GetUserMediaTask
> aTask
);
369 // Finds the task corresponding to aCallID and removes it from tracking.
370 RefPtr
<GetUserMediaTask
> TakeGetUserMediaTask(const nsAString
& aCallID
);
371 // Intended for use with "media.navigator.permission.disabled" to bypass the
372 // permission prompt and use the first appropriate device.
373 void NotifyAllowed(const nsString
& aCallID
,
374 const LocalMediaDeviceSet
& aDevices
);
377 MediaEngine
* GetBackend();
379 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf
);
381 // ONLY access from MainThread so we don't need to lock
382 WindowTable mActiveWindows
;
383 nsRefPtrHashtable
<nsStringHashKey
, GetUserMediaTask
> mActiveCallbacks
;
384 nsClassHashtable
<nsUint64HashKey
, nsTArray
<nsString
>> mCallIds
;
385 nsTArray
<RefPtr
<dom::GetUserMediaRequest
>> mPendingGUMRequest
;
386 // non-null if a device enumeration is in progress and was started after the
387 // last device-change invalidation
388 RefPtr
<media::Refcountable
<nsTArray
<MozPromiseHolder
<ConstDeviceSetPromise
>>>>
389 mPendingDevicesPromises
;
390 RefPtr
<MediaDeviceSetRefCnt
> mPhysicalDevices
;
391 TimeStamp mUnhandledDeviceChangeTime
;
392 RefPtr
<MediaTimer
> mDeviceChangeTimer
;
393 bool mCamerasMuted
= false;
394 bool mMicrophonesMuted
= false;
398 const RefPtr
<TaskQueue
> mMediaThread
;
401 nsCOMPtr
<nsIAsyncShutdownBlocker
> mShutdownBlocker
;
403 // ONLY accessed from MediaManagerThread
404 RefPtr
<MediaEngine
> mBackend
;
406 // Accessed only on main thread and mMediaThread.
407 // Set before mMediaThread is created, and cleared on main thread after last
408 // mMediaThread task is run.
409 static StaticRefPtr
<MediaManager
> sSingleton
;
411 // Connect/Disconnect on media thread only
412 MediaEventListener mDeviceListChangeListener
;
414 MediaEventProducer
<void> mDeviceListChangeEvent
;
417 RefPtr
<media::Parent
<media::NonE10s
>> mNonE10sParent
;
420 } // namespace mozilla
422 #endif // MOZILLA_MEDIAMANAGER_H