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"
36 # include "transport/runnable_utils.h"
39 class AudioDeviceInfo
;
44 class MediaEngineSource
;
49 struct AudioOutputOptions
;
50 struct MediaStreamConstraints
;
51 struct MediaTrackConstraints
;
52 struct MediaTrackConstraintSet
;
53 struct MediaTrackSettings
;
54 enum class CallerType
: uint32_t;
55 enum class MediaDeviceKind
: uint8_t;
62 class GetUserMediaTask
;
63 class GetUserMediaWindowListener
;
68 * Device info that is independent of any Window.
69 * MediaDevices can be shared, unlike LocalMediaDevices.
71 class MediaDevice final
{
73 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDevice
)
76 * Whether source device does end-run around cross origin restrictions.
78 enum class IsScary
{ No
, Yes
};
79 MediaDevice(MediaEngine
* aEngine
, dom::MediaSourceEnum aMediaSource
,
80 const nsString
& aRawName
, const nsString
& aRawID
,
81 const nsString
& aRawGroupID
, IsScary aIsScary
);
83 MediaDevice(MediaEngine
* aEngine
,
84 const RefPtr
<AudioDeviceInfo
>& aAudioDeviceInfo
,
85 const nsString
& aRawID
);
87 static RefPtr
<MediaDevice
> CopyWithNewRawGroupId(
88 const RefPtr
<MediaDevice
>& aOther
, const nsString
& aRawGroupID
);
90 dom::MediaSourceEnum
GetMediaSource() const;
96 const RefPtr
<MediaEngine
> mEngine
;
97 const RefPtr
<AudioDeviceInfo
> mAudioDeviceInfo
;
98 const dom::MediaSourceEnum mMediaSource
;
99 const dom::MediaDeviceKind mKind
;
102 const nsString mType
;
103 const nsString mRawID
;
104 const nsString mRawGroupID
;
105 const nsString mRawName
;
109 * Device info that is specific to a particular Window. If the device is a
110 * source device, then a single corresponding MediaEngineSource is provided,
111 * which can provide a maximum of one capture stream. LocalMediaDevices are
112 * not shared, but APIs returning LocalMediaDevices return a new object each
115 class LocalMediaDevice final
: public nsIMediaDevice
{
117 NS_DECL_THREADSAFE_ISUPPORTS
118 NS_DECL_NSIMEDIADEVICE
120 LocalMediaDevice(RefPtr
<const MediaDevice
> aRawDevice
, const nsString
& aID
,
121 const nsString
& aGroupID
, const nsString
& aName
);
123 uint32_t GetBestFitnessDistance(
124 const nsTArray
<const NormalizedConstraintSet
*>& aConstraintSets
,
125 dom::CallerType aCallerType
);
127 nsresult
Allocate(const dom::MediaTrackConstraints
& aConstraints
,
128 const MediaEnginePrefs
& aPrefs
, uint64_t aWindowId
,
129 const char** aOutBadConstraint
);
130 void SetTrack(const RefPtr
<MediaTrack
>& aTrack
,
131 const nsMainThreadPtrHandle
<nsIPrincipal
>& aPrincipal
);
133 nsresult
Reconfigure(const dom::MediaTrackConstraints
& aConstraints
,
134 const MediaEnginePrefs
& aPrefs
,
135 const char** aOutBadConstraint
);
136 nsresult
FocusOnSelectedSource();
138 nsresult
Deallocate();
140 void GetSettings(dom::MediaTrackSettings
& aOutSettings
);
141 MediaEngineSource
* Source();
142 // Returns null if not a physical audio device.
143 AudioDeviceInfo
* GetAudioDeviceInfo() const {
144 return mRawDevice
->mAudioDeviceInfo
;
146 dom::MediaSourceEnum
GetMediaSource() const {
147 return mRawDevice
->GetMediaSource();
149 dom::MediaDeviceKind
Kind() const { return mRawDevice
->mKind
; }
150 bool IsFake() const { return mRawDevice
->mIsFake
; }
151 const nsString
& RawID() { return mRawDevice
->mRawID
; }
154 virtual ~LocalMediaDevice() = default;
156 static uint32_t FitnessDistance(
158 const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters
&
161 static bool StringsContain(const dom::OwningStringOrStringSequence
& aStrings
,
163 static uint32_t FitnessDistance(
164 nsString aN
, const dom::ConstrainDOMStringParameters
& aParams
);
167 const RefPtr
<const MediaDevice
> mRawDevice
;
168 const nsString mName
;
170 const nsString mGroupID
;
173 RefPtr
<MediaEngineSource
> mSource
;
176 typedef nsRefPtrHashtable
<nsUint64HashKey
, GetUserMediaWindowListener
>
179 class MediaManager final
: public nsIMediaManagerService
,
180 public nsIMemoryReporter
,
182 friend DeviceListener
;
185 static already_AddRefed
<MediaManager
> GetInstance();
187 // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
188 // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
189 // from MediaManager thread.
190 static MediaManager
* Get();
191 static MediaManager
* GetIfExists();
192 static void StartupInit();
193 static void Dispatch(already_AddRefed
<Runnable
> task
);
196 * Posts an async operation to the media manager thread.
197 * FunctionType must be a function that takes a `MozPromiseHolder&`.
199 * The returned promise is resolved or rejected by aFunction on the media
202 template <typename MozPromiseType
, typename FunctionType
>
203 static RefPtr
<MozPromiseType
> Dispatch(const char* aName
,
204 FunctionType
&& aFunction
);
207 static bool IsInMediaThread();
210 static bool Exists() { return !!GetIfExists(); }
212 static nsresult
NotifyRecordingStatusChange(nsPIDOMWindowInner
* aWindow
);
214 NS_DECL_THREADSAFE_ISUPPORTS
216 NS_DECL_NSIMEMORYREPORTER
217 NS_DECL_NSIMEDIAMANAGERSERVICE
219 media::Parent
<media::NonE10s
>* GetNonE10sParent();
221 // If the window has not been destroyed, then return the
222 // GetUserMediaWindowListener for this window.
223 // If the window has been destroyed, then return null.
224 RefPtr
<GetUserMediaWindowListener
> GetOrMakeWindowListener(
225 nsPIDOMWindowInner
* aWindow
);
226 WindowTable
* GetActiveWindows() {
227 MOZ_ASSERT(NS_IsMainThread());
228 return &mActiveWindows
;
230 GetUserMediaWindowListener
* GetWindowListener(uint64_t aWindowId
) {
231 MOZ_ASSERT(NS_IsMainThread());
232 return mActiveWindows
.GetWeak(aWindowId
);
234 void AddWindowID(uint64_t aWindowId
,
235 RefPtr
<GetUserMediaWindowListener
> aListener
);
236 void RemoveWindowID(uint64_t aWindowId
);
237 void SendPendingGUMRequest();
238 bool IsWindowStillActive(uint64_t aWindowId
) {
239 return !!GetWindowListener(aWindowId
);
241 bool IsWindowListenerStillActive(
242 const RefPtr
<GetUserMediaWindowListener
>& aListener
);
244 static bool IsOn(const dom::OwningBooleanOrMediaTrackConstraints
& aUnion
) {
245 return !aUnion
.IsBoolean() || aUnion
.GetAsBoolean();
247 using GetUserMediaSuccessCallback
= dom::NavigatorUserMediaSuccessCallback
;
248 using GetUserMediaErrorCallback
= dom::NavigatorUserMediaErrorCallback
;
251 static void CallOnError(GetUserMediaErrorCallback
& aCallback
,
252 dom::MediaStreamError
& aError
);
254 static void CallOnSuccess(GetUserMediaSuccessCallback
& aCallback
,
255 DOMMediaStream
& aTrack
);
257 using MediaDeviceSet
= nsTArray
<RefPtr
<MediaDevice
>>;
258 using MediaDeviceSetRefCnt
= media::Refcountable
<MediaDeviceSet
>;
259 using LocalMediaDeviceSet
= nsTArray
<RefPtr
<LocalMediaDevice
>>;
260 using LocalMediaDeviceSetRefCnt
= media::Refcountable
<LocalMediaDeviceSet
>;
262 using StreamPromise
=
263 MozPromise
<RefPtr
<DOMMediaStream
>, RefPtr
<MediaMgrError
>, true>;
264 using DeviceSetPromise
=
265 MozPromise
<RefPtr
<MediaDeviceSetRefCnt
>, RefPtr
<MediaMgrError
>, true>;
266 using ConstDeviceSetPromise
= MozPromise
<RefPtr
<const MediaDeviceSetRefCnt
>,
267 RefPtr
<MediaMgrError
>, true>;
268 using LocalDevicePromise
=
269 MozPromise
<RefPtr
<LocalMediaDevice
>, RefPtr
<MediaMgrError
>, true>;
270 using LocalDeviceSetPromise
= MozPromise
<RefPtr
<LocalMediaDeviceSetRefCnt
>,
271 RefPtr
<MediaMgrError
>, true>;
272 using MgrPromise
= MozPromise
<bool, RefPtr
<MediaMgrError
>, true>;
274 RefPtr
<StreamPromise
> GetUserMedia(
275 nsPIDOMWindowInner
* aWindow
,
276 const dom::MediaStreamConstraints
& aConstraints
,
277 dom::CallerType aCallerType
);
279 RefPtr
<LocalDevicePromise
> SelectAudioOutput(
280 nsPIDOMWindowInner
* aWindow
, const dom::AudioOutputOptions
& aOptions
,
281 dom::CallerType aCallerType
);
283 // Return the list of microphone, camera, and speaker devices.
284 // MediaDeviceSets provided on promise resolution are shared between
285 // callers and so cannot be modified.
286 RefPtr
<ConstDeviceSetPromise
> GetPhysicalDevices();
288 void OnNavigation(uint64_t aWindowID
);
289 void OnCameraMute(bool aMute
);
290 void OnMicrophoneMute(bool aMute
);
291 bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId
);
293 MediaEventSource
<void>& DeviceListChangeEvent() {
294 return mDeviceListChangeEvent
;
296 RefPtr
<LocalDeviceSetPromise
> AnonymizeDevices(
297 nsPIDOMWindowInner
* aWindow
, RefPtr
<const MediaDeviceSetRefCnt
> aDevices
);
299 MediaEnginePrefs mPrefs
;
302 static nsresult
GenerateUUID(nsAString
& aResult
);
303 static nsresult
AnonymizeId(nsAString
& aId
, const nsACString
& aOriginKey
);
307 * This function tries to guess the group id for a video device in aDevices
308 * based on the device name. If the name of only one audio device in aAudios
309 * contains the name of the video device, then, this video device will take
310 * the group id of the audio device. Since this is a guess we try to minimize
311 * the probability of false positive. If we fail to find a correlation we
312 * leave the video group id untouched. In that case the group id will be the
315 static void GuessVideoDeviceGroupIDs(MediaDeviceSet
& aDevices
,
316 const MediaDeviceSet
& aAudios
);
319 enum class EnumerationFlag
{
320 AllowPermissionRequest
,
321 EnumerateAudioOutputs
,
324 using EnumerationFlags
= EnumSet
<EnumerationFlag
>;
325 RefPtr
<LocalDeviceSetPromise
> EnumerateDevicesImpl(
326 nsPIDOMWindowInner
* aWindow
, dom::MediaSourceEnum aVideoInputType
,
327 dom::MediaSourceEnum aAudioInputType
, EnumerationFlags aFlags
);
329 RefPtr
<DeviceSetPromise
> EnumerateRawDevices(
330 dom::MediaSourceEnum aVideoInputType
,
331 dom::MediaSourceEnum aAudioInputType
, EnumerationFlags aFlags
);
333 RefPtr
<LocalDeviceSetPromise
> SelectSettings(
334 const dom::MediaStreamConstraints
& aConstraints
,
335 dom::CallerType aCallerType
, RefPtr
<LocalMediaDeviceSetRefCnt
> aDevices
);
337 void GetPref(nsIPrefBranch
* aBranch
, const char* aPref
, const char* aData
,
339 void GetPrefBool(nsIPrefBranch
* aBranch
, const char* aPref
, const char* aData
,
341 void GetPrefs(nsIPrefBranch
* aBranch
, const char* aData
);
343 // Make private because we want only one instance of this class
344 explicit MediaManager(already_AddRefed
<TaskQueue
> aMediaThread
);
346 ~MediaManager() = default;
349 void StopScreensharing(uint64_t aWindowID
);
351 void RemoveMediaDevicesCallback(uint64_t aWindowID
);
352 void DeviceListChanged();
353 void InvalidateDeviceCache();
354 void HandleDeviceListChanged();
356 // Returns the number of incomplete tasks associated with this window,
357 // including the newly added task.
358 size_t AddTaskAndGetCount(uint64_t aWindowID
, const nsAString
& aCallID
,
359 RefPtr
<GetUserMediaTask
> aTask
);
360 // Finds the task corresponding to aCallID and removes it from tracking.
361 RefPtr
<GetUserMediaTask
> TakeGetUserMediaTask(const nsAString
& aCallID
);
362 // Intended for use with "media.navigator.permission.disabled" to bypass the
363 // permission prompt and use the first appropriate device.
364 void NotifyAllowed(const nsString
& aCallID
,
365 const LocalMediaDeviceSet
& aDevices
);
368 MediaEngine
* GetBackend();
370 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf
);
372 // ONLY access from MainThread so we don't need to lock
373 WindowTable mActiveWindows
;
374 nsRefPtrHashtable
<nsStringHashKey
, GetUserMediaTask
> mActiveCallbacks
;
375 nsClassHashtable
<nsUint64HashKey
, nsTArray
<nsString
>> mCallIds
;
376 nsTArray
<RefPtr
<dom::GetUserMediaRequest
>> mPendingGUMRequest
;
377 // non-null if a device enumeration is in progress and was started after the
378 // last device-change invalidation
379 RefPtr
<media::Refcountable
<nsTArray
<MozPromiseHolder
<ConstDeviceSetPromise
>>>>
380 mPendingDevicesPromises
;
381 RefPtr
<MediaDeviceSetRefCnt
> mPhysicalDevices
;
382 TimeStamp mUnhandledDeviceChangeTime
;
383 RefPtr
<MediaTimer
> mDeviceChangeTimer
;
384 bool mCamerasMuted
= false;
385 bool mMicrophonesMuted
= false;
388 const RefPtr
<TaskQueue
> mMediaThread
;
389 nsCOMPtr
<nsIAsyncShutdownBlocker
> mShutdownBlocker
;
391 // ONLY accessed from MediaManagerThread
392 RefPtr
<MediaEngine
> mBackend
;
394 // Accessed only on main thread and mMediaThread.
395 // Set before mMediaThread is created, and cleared on main thread after last
396 // mMediaThread task is run.
397 static StaticRefPtr
<MediaManager
> sSingleton
;
399 // Connect/Disconnect on media thread only
400 MediaEventListener mDeviceListChangeListener
;
402 MediaEventProducer
<void> mDeviceListChangeEvent
;
405 RefPtr
<media::Parent
<media::NonE10s
>> mNonE10sParent
;
408 } // namespace mozilla
410 #endif // MOZILLA_MEDIAMANAGER_H