MediaManager.h (16767B)
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/. */ 4 5 #ifndef MOZILLA_MEDIAMANAGER_H 6 #define MOZILLA_MEDIAMANAGER_H 7 8 #include "DOMMediaStream.h" 9 #include "MediaEnginePrefs.h" 10 #include "MediaEventSource.h" 11 #include "PerformanceRecorder.h" 12 #include "mozilla/Attributes.h" 13 #include "mozilla/Logging.h" 14 #include "mozilla/Preferences.h" 15 #include "mozilla/StaticPtr.h" 16 #include "mozilla/dom/GetUserMediaRequest.h" 17 #include "mozilla/dom/MediaStreamBinding.h" 18 #include "mozilla/dom/MediaStreamError.h" 19 #include "mozilla/dom/MediaStreamTrackBinding.h" 20 #include "mozilla/dom/MediaTrackCapabilitiesBinding.h" 21 #include "mozilla/dom/NavigatorBinding.h" 22 #include "mozilla/media/MediaChild.h" 23 #include "mozilla/media/MediaParent.h" 24 #include "nsClassHashtable.h" 25 #include "nsHashKeys.h" 26 #include "nsIMediaDevice.h" 27 #include "nsIMediaManager.h" 28 #include "nsIMemoryReporter.h" 29 #include "nsIObserver.h" 30 #include "nsRefPtrHashtable.h" 31 #include "nsXULAppAPI.h" 32 33 #ifdef MOZ_WEBRTC 34 # include "transport/runnable_utils.h" 35 #endif 36 37 class AudioDeviceInfo; 38 class nsIPrefBranch; 39 40 #ifdef MOZ_WEBRTC 41 class WebrtcLogSinkHandle; 42 #endif 43 44 namespace mozilla { 45 class MediaEngine; 46 class MediaEngineSource; 47 class TaskQueue; 48 class MediaTrack; 49 template <typename T> 50 class MediaTimer; 51 namespace dom { 52 struct AudioOutputOptions; 53 struct MediaStreamConstraints; 54 struct MediaTrackConstraints; 55 struct MediaTrackConstraintSet; 56 struct MediaTrackSettings; 57 enum class CallerType : uint32_t; 58 enum class MediaDeviceKind : uint8_t; 59 } // namespace dom 60 61 namespace ipc { 62 class PrincipalInfo; 63 } 64 65 class GetUserMediaTask; 66 class GetUserMediaWindowListener; 67 class MediaManager; 68 class DeviceListener; 69 70 /** 71 * Device info that is independent of any Window. 72 * MediaDevices can be shared, unlike LocalMediaDevices. 73 */ 74 class MediaDevice final { 75 public: 76 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDevice) 77 78 /** 79 * Whether source device does end-run around cross origin restrictions. 80 */ 81 enum class IsScary { No, Yes }; 82 83 /** 84 * Whether source device can use OS level selection prompt 85 */ 86 enum class OsPromptable { No, Yes }; 87 88 MediaDevice(MediaEngine* aEngine, dom::MediaSourceEnum aMediaSource, 89 const nsString& aRawName, const nsString& aRawID, 90 const nsString& aRawGroupID, IsScary aIsScary, 91 const OsPromptable canRequestOsLevelPrompt); 92 93 MediaDevice(MediaEngine* aEngine, 94 const RefPtr<AudioDeviceInfo>& aAudioDeviceInfo, 95 const nsString& aRawID); 96 97 static RefPtr<MediaDevice> CopyWithNewRawGroupId( 98 const RefPtr<MediaDevice>& aOther, const nsString& aRawGroupID); 99 100 dom::MediaSourceEnum GetMediaSource() const; 101 102 protected: 103 ~MediaDevice(); 104 105 public: 106 const RefPtr<MediaEngine> mEngine; 107 const RefPtr<AudioDeviceInfo> mAudioDeviceInfo; 108 const dom::MediaSourceEnum mMediaSource; 109 const dom::MediaDeviceKind mKind; 110 const bool mScary; 111 const bool mCanRequestOsLevelPrompt; 112 const bool mIsFake; 113 const nsString mType; 114 const nsString mRawID; 115 const nsString mRawGroupID; 116 const nsString mRawName; 117 }; 118 119 /** 120 * Device info that is specific to a particular Window. If the device is a 121 * source device, then a single corresponding MediaEngineSource is provided, 122 * which can provide a maximum of one capture stream. LocalMediaDevices are 123 * not shared, but APIs returning LocalMediaDevices return a new object each 124 * call. 125 */ 126 class LocalMediaDevice final : public nsIMediaDevice { 127 public: 128 NS_DECL_THREADSAFE_ISUPPORTS 129 NS_DECL_NSIMEDIADEVICE 130 131 LocalMediaDevice(RefPtr<const MediaDevice> aRawDevice, const nsString& aID, 132 const nsString& aGroupID, const nsString& aName); 133 134 uint32_t GetBestFitnessDistance( 135 const nsTArray<const NormalizedConstraintSet*>& aConstraintSets, 136 const MediaEnginePrefs& aPrefs, dom::CallerType aCallerType); 137 138 nsresult Allocate(const dom::MediaTrackConstraints& aConstraints, 139 const MediaEnginePrefs& aPrefs, uint64_t aWindowId, 140 const char** aOutBadConstraint); 141 void SetTrack(const RefPtr<mozilla::MediaTrack>& aTrack, 142 const nsMainThreadPtrHandle<nsIPrincipal>& aPrincipal); 143 nsresult Start(); 144 nsresult Reconfigure(const dom::MediaTrackConstraints& aConstraints, 145 const MediaEnginePrefs& aPrefs, 146 const char** aOutBadConstraint); 147 nsresult FocusOnSelectedSource(); 148 nsresult Stop(); 149 nsresult Deallocate(); 150 151 /** 152 * Clones the LocalMediaDevice and sets a cloned source. 153 */ 154 already_AddRefed<LocalMediaDevice> Clone() const; 155 156 void GetSettings(dom::MediaTrackSettings& aOutSettings); 157 void GetCapabilities(dom::MediaTrackCapabilities& aOutCapabilities); 158 MediaEngineSource* Source(); 159 const TrackingId& GetTrackingId() const; 160 // Returns null if not a physical audio device. 161 AudioDeviceInfo* GetAudioDeviceInfo() const { 162 return mRawDevice->mAudioDeviceInfo; 163 } 164 dom::MediaSourceEnum GetMediaSource() const { 165 return mRawDevice->GetMediaSource(); 166 } 167 dom::MediaDeviceKind Kind() const { return mRawDevice->mKind; } 168 bool IsFake() const { return mRawDevice->mIsFake; } 169 const nsString& RawID() { return mRawDevice->mRawID; } 170 const dom::MediaTrackConstraints& Constraints() const; 171 172 private: 173 virtual ~LocalMediaDevice() = default; 174 175 static uint32_t FitnessDistance( 176 nsString aN, 177 const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& 178 aConstraint); 179 180 static bool StringsContain(const dom::OwningStringOrStringSequence& aStrings, 181 nsString aN); 182 static uint32_t FitnessDistance( 183 nsString aN, const dom::ConstrainDOMStringParameters& aParams); 184 185 public: 186 const RefPtr<const MediaDevice> mRawDevice; 187 const nsString mName; 188 const nsString mID; 189 const nsString mGroupID; 190 191 private: 192 RefPtr<MediaEngineSource> mSource; 193 // Currently applied constraints. Media thread only. 194 dom::MediaTrackConstraints mConstraints; 195 }; 196 197 typedef nsRefPtrHashtable<nsUint64HashKey, GetUserMediaWindowListener> 198 WindowTable; 199 200 class MediaManager final : public nsIMediaManagerService, 201 public nsIMemoryReporter, 202 public nsIObserver { 203 friend DeviceListener; 204 205 public: 206 static already_AddRefed<MediaManager> GetInstance(); 207 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); 214 215 /** 216 * Posts an async operation to the media manager thread. 217 * FunctionType must be a function that takes a `MozPromiseHolder&`. 218 * 219 * The returned promise is resolved or rejected by aFunction on the media 220 * manager thread. 221 */ 222 template <typename MozPromiseType, typename FunctionType> 223 static RefPtr<MozPromiseType> Dispatch(StaticString aName, 224 FunctionType&& aFunction); 225 226 #ifdef DEBUG 227 static bool IsInMediaThread(); 228 #endif 229 230 static bool Exists() { return !!GetIfExists(); } 231 232 static nsresult NotifyRecordingStatusChange(nsPIDOMWindowInner* aWindow); 233 234 NS_DECL_THREADSAFE_ISUPPORTS 235 NS_DECL_NSIOBSERVER 236 NS_DECL_NSIMEMORYREPORTER 237 NS_DECL_NSIMEDIAMANAGERSERVICE 238 239 media::Parent<media::NonE10s>* GetNonE10sParent(); 240 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; 249 } 250 GetUserMediaWindowListener* GetWindowListener(uint64_t aWindowId) { 251 MOZ_ASSERT(NS_IsMainThread()); 252 return mActiveWindows.GetWeak(aWindowId); 253 } 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); 260 } 261 bool IsWindowListenerStillActive( 262 const RefPtr<GetUserMediaWindowListener>& aListener); 263 264 static bool IsOn(const dom::OwningBooleanOrMediaTrackConstraints& aUnion) { 265 return !aUnion.IsBoolean() || aUnion.GetAsBoolean(); 266 } 267 using GetUserMediaSuccessCallback = dom::NavigatorUserMediaSuccessCallback; 268 using GetUserMediaErrorCallback = dom::NavigatorUserMediaErrorCallback; 269 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); 276 277 using MediaDeviceSet = nsTArray<RefPtr<MediaDevice>>; 278 using MediaDeviceSetRefCnt = media::Refcountable<MediaDeviceSet>; 279 using LocalMediaDeviceSet = nsTArray<RefPtr<LocalMediaDevice>>; 280 using LocalMediaDeviceSetRefCnt = media::Refcountable<LocalMediaDeviceSet>; 281 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>; 293 294 RefPtr<StreamPromise> GetUserMedia( 295 nsPIDOMWindowInner* aWindow, 296 const dom::MediaStreamConstraints& aConstraints, 297 dom::CallerType aCallerType); 298 299 RefPtr<LocalDevicePromise> SelectAudioOutput( 300 nsPIDOMWindowInner* aWindow, const dom::AudioOutputOptions& aOptions, 301 dom::CallerType aCallerType); 302 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(); 307 308 void OnNavigation(uint64_t aWindowID); 309 void OnCameraMute(bool aMute); 310 void OnMicrophoneMute(bool aMute); 311 bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId); 312 313 MediaEventSource<void>& DeviceListChangeEvent() { 314 return mDeviceListChangeEvent; 315 } 316 RefPtr<LocalDeviceSetPromise> AnonymizeDevices( 317 nsPIDOMWindowInner* aWindow, RefPtr<const MediaDeviceSetRefCnt> aDevices); 318 319 MediaEnginePrefs mPrefs; 320 321 private: 322 static nsresult GenerateUUID(nsAString& aResult); 323 324 public: 325 /** 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. 333 */ 334 static void GuessVideoDeviceGroupIDs(MediaDeviceSet& aDevices, 335 const MediaDeviceSet& aAudios); 336 337 private: 338 enum class EnumerationFlag { 339 AllowPermissionRequest, 340 EnumerateAudioOutputs, 341 ForceFakes, 342 }; 343 using EnumerationFlags = EnumSet<EnumerationFlag>; 344 345 enum class DeviceType { Real, Fake }; 346 347 struct DeviceEnumerationParams { 348 DeviceEnumerationParams(dom::MediaSourceEnum aInputType, DeviceType aType, 349 nsAutoCString aForcedDeviceName); 350 dom::MediaSourceEnum mInputType; 351 DeviceType mType; 352 nsAutoCString mForcedDeviceName; 353 }; 354 355 struct VideoDeviceEnumerationParams : public DeviceEnumerationParams { 356 VideoDeviceEnumerationParams(dom::MediaSourceEnum aInputType, 357 DeviceType aType, 358 nsAutoCString aForcedDeviceName, 359 nsAutoCString aForcedMicrophoneName); 360 361 // The by-pref forced microphone device name, used for groupId correlation 362 // of camera devices. 363 nsAutoCString mForcedMicrophoneName; 364 }; 365 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; 378 }; 379 380 static EnumerationParams CreateEnumerationParams( 381 dom::MediaSourceEnum aVideoInputType, 382 dom::MediaSourceEnum aAudioInputType, EnumerationFlags aFlags); 383 384 RefPtr<LocalDeviceSetPromise> EnumerateDevicesImpl( 385 nsPIDOMWindowInner* aWindow, EnumerationParams aParams); 386 387 RefPtr<DeviceSetPromise> MaybeRequestPermissionAndEnumerateRawDevices( 388 EnumerationParams aParams); 389 390 static RefPtr<MediaDeviceSetRefCnt> EnumerateRawDevices( 391 EnumerationParams aParams); 392 393 RefPtr<LocalDeviceSetPromise> SelectSettings( 394 const dom::MediaStreamConstraints& aConstraints, 395 dom::CallerType aCallerType, RefPtr<LocalMediaDeviceSetRefCnt> aDevices); 396 397 nsresult GetPref(nsIPrefBranch* aBranch, const char* aPref, const char* aData, 398 int32_t* aVal); 399 nsresult GetPrefBool(nsIPrefBranch* aBranch, const char* aPref, 400 const char* aData, bool* aVal); 401 void GetPrefs(nsIPrefBranch* aBranch, const char* aData); 402 403 // Make private because we want only one instance of this class 404 explicit MediaManager(already_AddRefed<TaskQueue> aMediaThread); 405 406 ~MediaManager() = default; 407 void Shutdown(); 408 409 void StopScreensharing(uint64_t aWindowID); 410 411 void RemoveMediaDevicesCallback(uint64_t aWindowID); 412 void DeviceListChanged(); 413 void EnsureNoPlaceholdersInDeviceCache(); 414 void InvalidateDeviceCache(); 415 void HandleDeviceListChanged(); 416 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); 427 428 // Media thread only 429 MediaEngine* GetBackend(); 430 431 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf); 432 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<TimeStamp>> mDeviceChangeTimer; 448 bool mCamerasMuted = false; 449 bool mMicrophonesMuted = false; 450 451 public: 452 // Always exists 453 const RefPtr<TaskQueue> mMediaThread; 454 455 private: 456 nsCOMPtr<nsIAsyncShutdownBlocker> mShutdownBlocker; 457 458 // ONLY accessed from MediaManagerThread 459 RefPtr<MediaEngine> mBackend; 460 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; 465 466 // Connect/Disconnect on media thread only 467 MediaEventListener mDeviceListChangeListener; 468 469 MediaEventProducer<void> mDeviceListChangeEvent; 470 471 public: 472 RefPtr<media::Parent<media::NonE10s>> mNonE10sParent; 473 }; 474 475 } // namespace mozilla 476 477 #endif // MOZILLA_MEDIAMANAGER_H