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 "MediaEngine.h"
9 #include "MediaEnginePrefs.h"
10 #include "MediaEventSource.h"
11 #include "mozilla/dom/GetUserMediaRequest.h"
12 #include "mozilla/Unused.h"
13 #include "nsIMediaDevice.h"
14 #include "nsIMediaManager.h"
16 #include "nsHashKeys.h"
17 #include "nsClassHashtable.h"
18 #include "nsRefPtrHashtable.h"
19 #include "nsIMemoryReporter.h"
20 #include "nsIObserver.h"
22 #include "nsXULAppAPI.h"
23 #include "mozilla/Attributes.h"
24 #include "mozilla/Preferences.h"
25 #include "mozilla/StaticMutex.h"
26 #include "mozilla/StaticPtr.h"
27 #include "mozilla/dom/MediaStreamBinding.h"
28 #include "mozilla/dom/MediaStreamTrackBinding.h"
29 #include "mozilla/dom/MediaStreamError.h"
30 #include "mozilla/dom/NavigatorBinding.h"
31 #include "mozilla/media/MediaChild.h"
32 #include "mozilla/media/MediaParent.h"
33 #include "mozilla/Logging.h"
34 #include "mozilla/UniquePtr.h"
35 #include "DOMMediaStream.h"
38 # include "transport/runnable_utils.h"
47 struct AudioOutputOptions
;
48 struct MediaStreamConstraints
;
49 struct MediaTrackConstraints
;
50 struct MediaTrackConstraintSet
;
51 enum class CallerType
: uint32_t;
52 enum class MediaDeviceKind
: uint8_t;
59 class GetUserMediaTask
;
60 class GetUserMediaWindowListener
;
64 class MediaDevice
: public nsIMediaDevice
{
66 NS_DECL_THREADSAFE_ISUPPORTS
67 NS_DECL_NSIMEDIADEVICE
69 MediaDevice(const RefPtr
<MediaEngineSource
>& aSource
, const nsString
& aName
,
70 const nsString
& aID
, const nsString
& aGroupID
,
71 const nsString
& aRawID
);
73 MediaDevice(const RefPtr
<AudioDeviceInfo
>& aAudioDeviceInfo
,
74 const nsString
& aID
, const nsString
& aGroupID
,
75 const nsString
& aRawID
= u
""_ns
);
77 MediaDevice(const RefPtr
<MediaDevice
>& aOther
, const nsString
& aID
,
78 const nsString
& aGroupID
, const nsString
& aRawID
,
79 const nsString
& aRawGroupID
);
81 MediaDevice(const RefPtr
<MediaDevice
>& aOther
, const nsString
& aID
,
82 const nsString
& aGroupID
, const nsString
& aRawID
,
83 const nsString
& aRawGroupID
, const nsString
& aName
);
85 uint32_t GetBestFitnessDistance(
86 const nsTArray
<const NormalizedConstraintSet
*>& aConstraintSets
,
87 dom::CallerType aCallerType
);
89 nsresult
Allocate(const dom::MediaTrackConstraints
& aConstraints
,
90 const MediaEnginePrefs
& aPrefs
, uint64_t aWindowId
,
91 const char** aOutBadConstraint
);
92 void SetTrack(const RefPtr
<MediaTrack
>& aTrack
,
93 const PrincipalHandle
& aPrincipal
);
95 nsresult
Reconfigure(const dom::MediaTrackConstraints
& aConstraints
,
96 const MediaEnginePrefs
& aPrefs
,
97 const char** aOutBadConstraint
);
98 nsresult
FocusOnSelectedSource();
100 nsresult
Deallocate();
102 void GetSettings(dom::MediaTrackSettings
& aOutSettings
) const;
104 dom::MediaSourceEnum
GetMediaSource() const;
107 virtual ~MediaDevice() = default;
109 static uint32_t FitnessDistance(
111 const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters
&
115 static bool StringsContain(const dom::OwningStringOrStringSequence
& aStrings
,
117 static uint32_t FitnessDistance(
118 nsString aN
, const dom::ConstrainDOMStringParameters
& aParams
);
121 const RefPtr
<MediaEngineSource
> mSource
;
122 const RefPtr
<AudioDeviceInfo
> mSinkInfo
;
123 const dom::MediaDeviceKind mKind
;
126 const nsString mType
;
127 const nsString mName
;
129 const nsString mGroupID
;
130 const nsString mRawID
;
131 const nsString mRawGroupID
;
132 const nsString mRawName
;
135 typedef nsRefPtrHashtable
<nsUint64HashKey
, GetUserMediaWindowListener
>
137 typedef MozPromise
<RefPtr
<AudioDeviceInfo
>, nsresult
, true> SinkInfoPromise
;
139 class MediaManager final
: public nsIMediaManagerService
,
140 public nsIMemoryReporter
,
142 friend DeviceListener
;
145 static already_AddRefed
<MediaManager
> GetInstance();
147 // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
148 // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
149 // from MediaManager thread.
150 static MediaManager
* Get();
151 static MediaManager
* GetIfExists();
152 static void StartupInit();
153 static void Dispatch(already_AddRefed
<Runnable
> task
);
156 * Posts an async operation to the media manager thread.
157 * FunctionType must be a function that takes a `MozPromiseHolder&`.
159 * The returned promise is resolved or rejected by aFunction on the media
162 template <typename MozPromiseType
, typename FunctionType
>
163 static RefPtr
<MozPromiseType
> Dispatch(const char* aName
,
164 FunctionType
&& aFunction
);
167 static bool IsInMediaThread();
170 static bool Exists() { return !!GetIfExists(); }
172 static nsresult
NotifyRecordingStatusChange(nsPIDOMWindowInner
* aWindow
);
174 NS_DECL_THREADSAFE_ISUPPORTS
176 NS_DECL_NSIMEMORYREPORTER
177 NS_DECL_NSIMEDIAMANAGERSERVICE
179 media::Parent
<media::NonE10s
>* GetNonE10sParent();
180 MediaEngine
* GetBackend();
182 // If the window has not been destroyed, then return the
183 // GetUserMediaWindowListener for this window.
184 // If the window has been destroyed, then return null.
185 RefPtr
<GetUserMediaWindowListener
> GetOrMakeWindowListener(
186 nsPIDOMWindowInner
* aWindow
);
187 WindowTable
* GetActiveWindows() {
188 MOZ_ASSERT(NS_IsMainThread());
189 return &mActiveWindows
;
191 GetUserMediaWindowListener
* GetWindowListener(uint64_t aWindowId
) {
192 MOZ_ASSERT(NS_IsMainThread());
193 return mActiveWindows
.GetWeak(aWindowId
);
195 void AddWindowID(uint64_t aWindowId
,
196 RefPtr
<GetUserMediaWindowListener
> aListener
);
197 void RemoveWindowID(uint64_t aWindowId
);
198 void SendPendingGUMRequest();
199 bool IsWindowStillActive(uint64_t aWindowId
) {
200 return !!GetWindowListener(aWindowId
);
202 bool IsWindowListenerStillActive(
203 const RefPtr
<GetUserMediaWindowListener
>& aListener
);
205 static bool IsOn(const dom::OwningBooleanOrMediaTrackConstraints
& aUnion
) {
206 return !aUnion
.IsBoolean() || aUnion
.GetAsBoolean();
208 using GetUserMediaSuccessCallback
= dom::NavigatorUserMediaSuccessCallback
;
209 using GetUserMediaErrorCallback
= dom::NavigatorUserMediaErrorCallback
;
212 static void CallOnError(GetUserMediaErrorCallback
& aCallback
,
213 dom::MediaStreamError
& aError
);
215 static void CallOnSuccess(GetUserMediaSuccessCallback
& aCallback
,
216 DOMMediaStream
& aTrack
);
218 using MediaDeviceSet
= nsTArray
<RefPtr
<MediaDevice
>>;
219 using MediaDeviceSetRefCnt
= media::Refcountable
<MediaDeviceSet
>;
221 using StreamPromise
=
222 MozPromise
<RefPtr
<DOMMediaStream
>, RefPtr
<MediaMgrError
>, true>;
223 using DevicePromise
=
224 MozPromise
<RefPtr
<MediaDevice
>, RefPtr
<MediaMgrError
>, true>;
225 using DeviceSetPromise
=
226 MozPromise
<RefPtr
<MediaDeviceSetRefCnt
>, RefPtr
<MediaMgrError
>, true>;
227 using MgrPromise
= MozPromise
<bool, RefPtr
<MediaMgrError
>, true>;
228 using BadConstraintsPromise
=
229 MozPromise
<const char*, RefPtr
<MediaMgrError
>, true>;
231 RefPtr
<StreamPromise
> GetUserMedia(
232 nsPIDOMWindowInner
* aWindow
,
233 const dom::MediaStreamConstraints
& aConstraints
,
234 dom::CallerType aCallerType
);
236 RefPtr
<DeviceSetPromise
> EnumerateDevices(nsPIDOMWindowInner
* aWindow
,
237 dom::CallerType aCallerType
);
239 nsresult
EnumerateDevices(nsPIDOMWindowInner
* aWindow
,
240 dom::Promise
& aPromise
);
242 RefPtr
<DevicePromise
> SelectAudioOutput(
243 nsPIDOMWindowInner
* aWindow
, const dom::AudioOutputOptions
& aOptions
,
244 dom::CallerType aCallerType
);
245 // Get the sink that corresponds to the given device id.
246 // It is resposible to check if an application is
247 // authorized to play audio through the requested device.
248 // The returned promise will be resolved with the device
249 // information if the device id matches one and operation is
250 // allowed. The default device is always allowed. Non default
251 // devices are allowed only in secure context. It is pending to
252 // implement an user authorization model. The promise will be
253 // rejected in the following cases:
254 // NS_ERROR_NOT_AVAILABLE: Device id does not exist.
255 // NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR:
256 // The requested device exists but it is not allowed to be used.
257 // Currently, this happens only on non-default default devices
258 // and non https connections. TODO, authorization model to allow
259 // an application to play audio through the device (Bug 1493982).
260 // NS_ERROR_ABORT: General error.
261 RefPtr
<SinkInfoPromise
> GetSinkDevice(nsPIDOMWindowInner
* aWindow
,
262 const nsString
& aDeviceId
);
264 void OnNavigation(uint64_t aWindowID
);
265 void OnCameraMute(bool aMute
);
266 void OnMicrophoneMute(bool aMute
);
267 bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId
);
269 MediaEventSource
<void>& DeviceListChangeEvent() {
270 return mDeviceListChangeEvent
;
273 MediaEnginePrefs mPrefs
;
276 static nsresult
GenerateUUID(nsAString
& aResult
);
277 static nsresult
AnonymizeId(nsAString
& aId
, const nsACString
& aOriginKey
);
279 public: // TODO: make private once we upgrade to GCC 4.8+ on linux.
280 static void AnonymizeDevices(MediaDeviceSet
& aDevices
,
281 const nsACString
& aOriginKey
,
282 const uint64_t aWindowId
);
285 * This function tries to guess the group id for a video device in aDevices
286 * based on the device name. If the name of only one audio device in aAudios
287 * contains the name of the video device, then, this video device will take
288 * the group id of the audio device. Since this is a guess we try to minimize
289 * the probability of false positive. If we fail to find a correlation we
290 * leave the video group id untouched. In that case the group id will be the
293 static void GuessVideoDeviceGroupIDs(MediaDeviceSet
& aDevices
,
294 const MediaDeviceSet
& aAudios
);
297 enum class DeviceEnumerationType
: uint8_t {
298 Normal
, // Enumeration should not return loopback or fake devices
299 Fake
, // Enumeration should return fake device(s)
300 Loopback
/* Enumeration should return loopback device(s) (possibly in
301 addition to normal devices) */
304 RefPtr
<MgrPromise
> EnumerateRawDevices(
305 uint64_t aWindowId
, dom::MediaSourceEnum aVideoInputType
,
306 dom::MediaSourceEnum aAudioInputType
, MediaSinkEnum aAudioOutputType
,
307 DeviceEnumerationType aVideoInputEnumType
,
308 DeviceEnumerationType aAudioInputEnumType
, bool aForceNoPermRequest
,
309 const RefPtr
<MediaDeviceSetRefCnt
>& aOutDevices
);
311 RefPtr
<MgrPromise
> EnumerateDevicesImpl(
312 nsPIDOMWindowInner
* aWindow
, dom::MediaSourceEnum aVideoInputType
,
313 dom::MediaSourceEnum aAudioInputType
, MediaSinkEnum aAudioOutputType
,
314 DeviceEnumerationType aVideoInputEnumType
,
315 DeviceEnumerationType aAudioInputEnumType
, bool aForceNoPermRequest
,
316 const RefPtr
<MediaDeviceSetRefCnt
>& aOutDevices
);
318 RefPtr
<BadConstraintsPromise
> SelectSettings(
319 const dom::MediaStreamConstraints
& aConstraints
,
320 dom::CallerType aCallerType
,
321 const RefPtr
<MediaDeviceSetRefCnt
>& aSources
);
323 void GetPref(nsIPrefBranch
* aBranch
, const char* aPref
, const char* aData
,
325 void GetPrefBool(nsIPrefBranch
* aBranch
, const char* aPref
, const char* aData
,
327 void GetPrefs(nsIPrefBranch
* aBranch
, const char* aData
);
329 // Make private because we want only one instance of this class
330 explicit MediaManager(already_AddRefed
<TaskQueue
> aMediaThread
);
332 ~MediaManager() = default;
335 void StopScreensharing(uint64_t aWindowID
);
337 void RemoveMediaDevicesCallback(uint64_t aWindowID
);
338 void DeviceListChanged();
340 // Returns the number of incomplete tasks associated with this window,
341 // including the newly added task.
342 size_t AddTaskAndGetCount(uint64_t aWindowID
, const nsAString
& aCallID
,
343 RefPtr
<GetUserMediaTask
> aTask
);
344 // Finds the task corresponding to aCallID and removes it from tracking.
345 RefPtr
<GetUserMediaTask
> TakeGetUserMediaTask(const nsAString
& aCallID
);
346 // Intended for use with "media.navigator.permission.disabled" to bypass the
347 // permission prompt and use the first appropriate device.
348 void NotifyAllowed(const nsString
& aCallID
, const MediaDeviceSet
& aDevices
);
350 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf
);
352 struct nsStringHasher
{
353 using Key
= nsString
;
354 using Lookup
= nsString
;
356 static HashNumber
hash(const Lookup
& aLookup
) {
357 return HashString(aLookup
.get());
360 static bool match(const Key
& aKey
, const Lookup
& aLookup
) {
361 return aKey
== aLookup
;
365 // ONLY access from MainThread so we don't need to lock
366 WindowTable mActiveWindows
;
367 nsRefPtrHashtable
<nsStringHashKey
, GetUserMediaTask
> mActiveCallbacks
;
368 nsClassHashtable
<nsUint64HashKey
, nsTArray
<nsString
>> mCallIds
;
369 nsTArray
<RefPtr
<dom::GetUserMediaRequest
>> mPendingGUMRequest
;
370 using DeviceIdSet
= HashSet
<nsString
, nsStringHasher
, InfallibleAllocPolicy
>;
371 DeviceIdSet mDeviceIDs
;
372 RefPtr
<MediaTimer
> mDeviceChangeTimer
;
373 bool mCamerasMuted
= false;
374 bool mMicrophonesMuted
= false;
377 const RefPtr
<TaskQueue
> mMediaThread
;
378 nsCOMPtr
<nsIAsyncShutdownBlocker
> mShutdownBlocker
;
380 // ONLY accessed from MediaManagerThread
381 RefPtr
<MediaEngine
> mBackend
;
383 static StaticRefPtr
<MediaManager
> sSingleton
;
384 static StaticMutex sSingletonMutex
;
386 // Connect/Disconnect on media thread only
387 MediaEventListener mDeviceListChangeListener
;
389 MediaEventProducer
<void> mDeviceListChangeEvent
;
392 RefPtr
<media::Parent
<media::NonE10s
>> mNonE10sParent
;
395 } // namespace mozilla
397 #endif // MOZILLA_MEDIAMANAGER_H