Backed out changeset 06f41c22f3a6 (bug 1888460) for causing linux xpcshell failures...
[gecko.git] / dom / media / MediaManager.h
blob738ccd795d41e4799f6c6ecac3024913c0013efd
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"
36 #ifdef MOZ_WEBRTC
37 # include "transport/runnable_utils.h"
38 #endif
40 class AudioDeviceInfo;
41 class nsIPrefBranch;
43 #ifdef MOZ_WEBRTC
44 class WebrtcLogSinkHandle;
45 #endif
47 namespace mozilla {
48 class MediaEngine;
49 class MediaEngineSource;
50 class TaskQueue;
51 class MediaTimer;
52 class MediaTrack;
53 namespace dom {
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;
61 } // namespace dom
63 namespace ipc {
64 class PrincipalInfo;
67 class GetUserMediaTask;
68 class GetUserMediaWindowListener;
69 class MediaManager;
70 class DeviceListener;
72 /**
73 * Device info that is independent of any Window.
74 * MediaDevices can be shared, unlike LocalMediaDevices.
76 class MediaDevice final {
77 public:
78 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDevice)
80 /**
81 * Whether source device does end-run around cross origin restrictions.
83 enum class IsScary { No, Yes };
85 /**
86 * Whether source device can use OS level selection prompt
88 enum class OsPromptable { No, Yes };
90 /**
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;
110 protected:
111 ~MediaDevice();
113 public:
114 const RefPtr<MediaEngine> mEngine;
115 const RefPtr<AudioDeviceInfo> mAudioDeviceInfo;
116 const dom::MediaSourceEnum mMediaSource;
117 const dom::MediaDeviceKind mKind;
118 const bool mScary;
119 const bool mCanRequestOsLevelPrompt;
120 const bool mIsFake;
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
133 * call.
135 class LocalMediaDevice final : public nsIMediaDevice {
136 public:
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);
152 nsresult Start();
153 nsresult Reconfigure(const dom::MediaTrackConstraints& aConstraints,
154 const MediaEnginePrefs& aPrefs,
155 const char** aOutBadConstraint);
156 nsresult FocusOnSelectedSource();
157 nsresult Stop();
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; }
174 private:
175 virtual ~LocalMediaDevice() = default;
177 static uint32_t FitnessDistance(
178 nsString aN,
179 const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters&
180 aConstraint);
182 static bool StringsContain(const dom::OwningStringOrStringSequence& aStrings,
183 nsString aN);
184 static uint32_t FitnessDistance(
185 nsString aN, const dom::ConstrainDOMStringParameters& aParams);
187 public:
188 const RefPtr<const MediaDevice> mRawDevice;
189 const nsString mName;
190 const nsString mID;
191 const nsString mGroupID;
193 private:
194 RefPtr<MediaEngineSource> mSource;
197 typedef nsRefPtrHashtable<nsUint64HashKey, GetUserMediaWindowListener>
198 WindowTable;
200 class MediaManager final : public nsIMediaManagerService,
201 public nsIMemoryReporter,
202 public nsIObserver {
203 friend DeviceListener;
205 public:
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
220 * manager thread.
222 template <typename MozPromiseType, typename FunctionType>
223 static RefPtr<MozPromiseType> Dispatch(const char* aName,
224 FunctionType&& aFunction);
226 #ifdef DEBUG
227 static bool IsInMediaThread();
228 #endif
230 static bool Exists() { return !!GetIfExists(); }
232 static nsresult NotifyRecordingStatusChange(nsPIDOMWindowInner* aWindow);
234 NS_DECL_THREADSAFE_ISUPPORTS
235 NS_DECL_NSIOBSERVER
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;
270 MOZ_CAN_RUN_SCRIPT
271 static void CallOnError(GetUserMediaErrorCallback& aCallback,
272 dom::MediaStreamError& aError);
273 MOZ_CAN_RUN_SCRIPT
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;
321 private:
322 static nsresult GenerateUUID(nsAString& aResult);
324 public:
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
332 * video device name.
334 static void GuessVideoDeviceGroupIDs(MediaDeviceSet& aDevices,
335 const MediaDeviceSet& aAudios);
337 private:
338 enum class EnumerationFlag {
339 AllowPermissionRequest,
340 EnumerateAudioOutputs,
341 ForceFakes,
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;
351 DeviceType mType;
352 nsAutoCString mForcedDeviceName;
355 struct VideoDeviceEnumerationParams : public DeviceEnumerationParams {
356 VideoDeviceEnumerationParams(dom::MediaSourceEnum aInputType,
357 DeviceType aType,
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,
398 int32_t* aVal);
399 void GetPrefBool(nsIPrefBranch* aBranch, const char* aPref, const char* aData,
400 bool* aVal);
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;
407 void Shutdown();
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);
428 // Media thread only
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;
438 #ifdef MOZ_WEBRTC
439 RefPtr<WebrtcLogSinkHandle> mLogHandle;
440 #endif
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;
451 public:
452 // Always exists
453 const RefPtr<TaskQueue> mMediaThread;
455 private:
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;
471 public:
472 RefPtr<media::Parent<media::NonE10s>> mNonE10sParent;
475 } // namespace mozilla
477 #endif // MOZILLA_MEDIAMANAGER_H