tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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