summaryrefslogtreecommitdiffstats
path: root/dom/media/MediaManager.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/MediaManager.h')
-rw-r--r--dom/media/MediaManager.h422
1 files changed, 422 insertions, 0 deletions
diff --git a/dom/media/MediaManager.h b/dom/media/MediaManager.h
new file mode 100644
index 0000000000..5a1679deec
--- /dev/null
+++ b/dom/media/MediaManager.h
@@ -0,0 +1,422 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_MEDIAMANAGER_H
+#define MOZILLA_MEDIAMANAGER_H
+
+#include "MediaEnginePrefs.h"
+#include "MediaEventSource.h"
+#include "mozilla/dom/GetUserMediaRequest.h"
+#include "mozilla/Unused.h"
+#include "nsIMediaDevice.h"
+#include "nsIMediaManager.h"
+
+#include "nsHashKeys.h"
+#include "nsClassHashtable.h"
+#include "nsRefPtrHashtable.h"
+#include "nsIMemoryReporter.h"
+#include "nsIObserver.h"
+
+#include "nsXULAppAPI.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/dom/MediaStreamBinding.h"
+#include "mozilla/dom/MediaStreamTrackBinding.h"
+#include "mozilla/dom/MediaStreamError.h"
+#include "mozilla/dom/NavigatorBinding.h"
+#include "mozilla/media/MediaChild.h"
+#include "mozilla/media/MediaParent.h"
+#include "mozilla/Logging.h"
+#include "mozilla/UniquePtr.h"
+#include "DOMMediaStream.h"
+#include "PerformanceRecorder.h"
+
+#ifdef MOZ_WEBRTC
+# include "transport/runnable_utils.h"
+#endif
+
+class AudioDeviceInfo;
+class nsIPrefBranch;
+
+namespace mozilla {
+class MediaEngine;
+class MediaEngineSource;
+class TaskQueue;
+class MediaTimer;
+class MediaTrack;
+namespace dom {
+struct AudioOutputOptions;
+struct MediaStreamConstraints;
+struct MediaTrackConstraints;
+struct MediaTrackConstraintSet;
+struct MediaTrackSettings;
+enum class CallerType : uint32_t;
+enum class MediaDeviceKind : uint8_t;
+} // namespace dom
+
+namespace ipc {
+class PrincipalInfo;
+}
+
+class GetUserMediaTask;
+class GetUserMediaWindowListener;
+class MediaManager;
+class DeviceListener;
+
+/**
+ * Device info that is independent of any Window.
+ * MediaDevices can be shared, unlike LocalMediaDevices.
+ */
+class MediaDevice final {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDevice)
+
+ /**
+ * Whether source device does end-run around cross origin restrictions.
+ */
+ enum class IsScary { No, Yes };
+
+ /**
+ * Whether source device can use OS level selection prompt
+ */
+ enum class OsPromptable { No, Yes };
+
+ MediaDevice(MediaEngine* aEngine, dom::MediaSourceEnum aMediaSource,
+ const nsString& aRawName, const nsString& aRawID,
+ const nsString& aRawGroupID, IsScary aIsScary,
+ const OsPromptable canRequestOsLevelPrompt);
+
+ MediaDevice(MediaEngine* aEngine,
+ const RefPtr<AudioDeviceInfo>& aAudioDeviceInfo,
+ const nsString& aRawID);
+
+ static RefPtr<MediaDevice> CopyWithNewRawGroupId(
+ const RefPtr<MediaDevice>& aOther, const nsString& aRawGroupID);
+
+ dom::MediaSourceEnum GetMediaSource() const;
+
+ protected:
+ ~MediaDevice();
+
+ public:
+ const RefPtr<MediaEngine> mEngine;
+ const RefPtr<AudioDeviceInfo> mAudioDeviceInfo;
+ const dom::MediaSourceEnum mMediaSource;
+ const dom::MediaDeviceKind mKind;
+ const bool mScary;
+ const bool mCanRequestOsLevelPrompt;
+ const bool mIsFake;
+ const nsString mType;
+ const nsString mRawID;
+ const nsString mRawGroupID;
+ const nsString mRawName;
+};
+
+/**
+ * Device info that is specific to a particular Window. If the device is a
+ * source device, then a single corresponding MediaEngineSource is provided,
+ * which can provide a maximum of one capture stream. LocalMediaDevices are
+ * not shared, but APIs returning LocalMediaDevices return a new object each
+ * call.
+ */
+class LocalMediaDevice final : public nsIMediaDevice {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIMEDIADEVICE
+
+ LocalMediaDevice(RefPtr<const MediaDevice> aRawDevice, const nsString& aID,
+ const nsString& aGroupID, const nsString& aName);
+
+ uint32_t GetBestFitnessDistance(
+ const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
+ dom::CallerType aCallerType);
+
+ nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
+ const MediaEnginePrefs& aPrefs, uint64_t aWindowId,
+ const char** aOutBadConstraint);
+ void SetTrack(const RefPtr<MediaTrack>& aTrack,
+ const nsMainThreadPtrHandle<nsIPrincipal>& aPrincipal);
+ nsresult Start();
+ nsresult Reconfigure(const dom::MediaTrackConstraints& aConstraints,
+ const MediaEnginePrefs& aPrefs,
+ const char** aOutBadConstraint);
+ nsresult FocusOnSelectedSource();
+ nsresult Stop();
+ nsresult Deallocate();
+
+ void GetSettings(dom::MediaTrackSettings& aOutSettings);
+ MediaEngineSource* Source();
+ const TrackingId& GetTrackingId() const;
+ // Returns null if not a physical audio device.
+ AudioDeviceInfo* GetAudioDeviceInfo() const {
+ return mRawDevice->mAudioDeviceInfo;
+ }
+ dom::MediaSourceEnum GetMediaSource() const {
+ return mRawDevice->GetMediaSource();
+ }
+ dom::MediaDeviceKind Kind() const { return mRawDevice->mKind; }
+ bool IsFake() const { return mRawDevice->mIsFake; }
+ const nsString& RawID() { return mRawDevice->mRawID; }
+
+ private:
+ virtual ~LocalMediaDevice() = default;
+
+ static uint32_t FitnessDistance(
+ nsString aN,
+ const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters&
+ aConstraint);
+
+ static bool StringsContain(const dom::OwningStringOrStringSequence& aStrings,
+ nsString aN);
+ static uint32_t FitnessDistance(
+ nsString aN, const dom::ConstrainDOMStringParameters& aParams);
+
+ public:
+ const RefPtr<const MediaDevice> mRawDevice;
+ const nsString mName;
+ const nsString mID;
+ const nsString mGroupID;
+
+ private:
+ RefPtr<MediaEngineSource> mSource;
+};
+
+typedef nsRefPtrHashtable<nsUint64HashKey, GetUserMediaWindowListener>
+ WindowTable;
+
+class MediaManager final : public nsIMediaManagerService,
+ public nsIMemoryReporter,
+ public nsIObserver {
+ friend DeviceListener;
+
+ public:
+ static already_AddRefed<MediaManager> GetInstance();
+
+ // NOTE: never NS_DispatchAndSpinEventLoopUntilComplete to the MediaManager
+ // thread from the MainThread, as we NS_DispatchAndSpinEventLoopUntilComplete
+ // to MainThread from MediaManager thread.
+ static MediaManager* Get();
+ static MediaManager* GetIfExists();
+ static void StartupInit();
+ static void Dispatch(already_AddRefed<Runnable> task);
+
+ /**
+ * Posts an async operation to the media manager thread.
+ * FunctionType must be a function that takes a `MozPromiseHolder&`.
+ *
+ * The returned promise is resolved or rejected by aFunction on the media
+ * manager thread.
+ */
+ template <typename MozPromiseType, typename FunctionType>
+ static RefPtr<MozPromiseType> Dispatch(const char* aName,
+ FunctionType&& aFunction);
+
+#ifdef DEBUG
+ static bool IsInMediaThread();
+#endif
+
+ static bool Exists() { return !!GetIfExists(); }
+
+ static nsresult NotifyRecordingStatusChange(nsPIDOMWindowInner* aWindow);
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSIMEMORYREPORTER
+ NS_DECL_NSIMEDIAMANAGERSERVICE
+
+ media::Parent<media::NonE10s>* GetNonE10sParent();
+
+ // If the window has not been destroyed, then return the
+ // GetUserMediaWindowListener for this window.
+ // If the window has been destroyed, then return null.
+ RefPtr<GetUserMediaWindowListener> GetOrMakeWindowListener(
+ nsPIDOMWindowInner* aWindow);
+ WindowTable* GetActiveWindows() {
+ MOZ_ASSERT(NS_IsMainThread());
+ return &mActiveWindows;
+ }
+ GetUserMediaWindowListener* GetWindowListener(uint64_t aWindowId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mActiveWindows.GetWeak(aWindowId);
+ }
+ void AddWindowID(uint64_t aWindowId,
+ RefPtr<GetUserMediaWindowListener> aListener);
+ void RemoveWindowID(uint64_t aWindowId);
+ void SendPendingGUMRequest();
+ bool IsWindowStillActive(uint64_t aWindowId) {
+ return !!GetWindowListener(aWindowId);
+ }
+ bool IsWindowListenerStillActive(
+ const RefPtr<GetUserMediaWindowListener>& aListener);
+
+ static bool IsOn(const dom::OwningBooleanOrMediaTrackConstraints& aUnion) {
+ return !aUnion.IsBoolean() || aUnion.GetAsBoolean();
+ }
+ using GetUserMediaSuccessCallback = dom::NavigatorUserMediaSuccessCallback;
+ using GetUserMediaErrorCallback = dom::NavigatorUserMediaErrorCallback;
+
+ MOZ_CAN_RUN_SCRIPT
+ static void CallOnError(GetUserMediaErrorCallback& aCallback,
+ dom::MediaStreamError& aError);
+ MOZ_CAN_RUN_SCRIPT
+ static void CallOnSuccess(GetUserMediaSuccessCallback& aCallback,
+ DOMMediaStream& aTrack);
+
+ using MediaDeviceSet = nsTArray<RefPtr<MediaDevice>>;
+ using MediaDeviceSetRefCnt = media::Refcountable<MediaDeviceSet>;
+ using LocalMediaDeviceSet = nsTArray<RefPtr<LocalMediaDevice>>;
+ using LocalMediaDeviceSetRefCnt = media::Refcountable<LocalMediaDeviceSet>;
+
+ using StreamPromise =
+ MozPromise<RefPtr<DOMMediaStream>, RefPtr<MediaMgrError>, true>;
+ using DeviceSetPromise =
+ MozPromise<RefPtr<MediaDeviceSetRefCnt>, RefPtr<MediaMgrError>, true>;
+ using ConstDeviceSetPromise = MozPromise<RefPtr<const MediaDeviceSetRefCnt>,
+ RefPtr<MediaMgrError>, true>;
+ using LocalDevicePromise =
+ MozPromise<RefPtr<LocalMediaDevice>, RefPtr<MediaMgrError>, true>;
+ using LocalDeviceSetPromise = MozPromise<RefPtr<LocalMediaDeviceSetRefCnt>,
+ RefPtr<MediaMgrError>, true>;
+ using MgrPromise = MozPromise<bool, RefPtr<MediaMgrError>, true>;
+
+ RefPtr<StreamPromise> GetUserMedia(
+ nsPIDOMWindowInner* aWindow,
+ const dom::MediaStreamConstraints& aConstraints,
+ dom::CallerType aCallerType);
+
+ RefPtr<LocalDevicePromise> SelectAudioOutput(
+ nsPIDOMWindowInner* aWindow, const dom::AudioOutputOptions& aOptions,
+ dom::CallerType aCallerType);
+
+ // Return the list of microphone, camera, and speaker devices.
+ // MediaDeviceSets provided on promise resolution are shared between
+ // callers and so cannot be modified.
+ RefPtr<ConstDeviceSetPromise> GetPhysicalDevices();
+
+ void OnNavigation(uint64_t aWindowID);
+ void OnCameraMute(bool aMute);
+ void OnMicrophoneMute(bool aMute);
+ bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId);
+
+ MediaEventSource<void>& DeviceListChangeEvent() {
+ return mDeviceListChangeEvent;
+ }
+ RefPtr<LocalDeviceSetPromise> AnonymizeDevices(
+ nsPIDOMWindowInner* aWindow, RefPtr<const MediaDeviceSetRefCnt> aDevices);
+
+ MediaEnginePrefs mPrefs;
+
+ private:
+ static nsresult GenerateUUID(nsAString& aResult);
+
+ public:
+ /**
+ * This function tries to guess the group id for a video device in aDevices
+ * based on the device name. If the name of only one audio device in aAudios
+ * contains the name of the video device, then, this video device will take
+ * the group id of the audio device. Since this is a guess we try to minimize
+ * the probability of false positive. If we fail to find a correlation we
+ * leave the video group id untouched. In that case the group id will be the
+ * video device name.
+ */
+ static void GuessVideoDeviceGroupIDs(MediaDeviceSet& aDevices,
+ const MediaDeviceSet& aAudios);
+
+ private:
+ enum class EnumerationFlag {
+ AllowPermissionRequest,
+ EnumerateAudioOutputs,
+ ForceFakes,
+ };
+ using EnumerationFlags = EnumSet<EnumerationFlag>;
+ RefPtr<LocalDeviceSetPromise> EnumerateDevicesImpl(
+ nsPIDOMWindowInner* aWindow, dom::MediaSourceEnum aVideoInputType,
+ dom::MediaSourceEnum aAudioInputType, EnumerationFlags aFlags);
+
+ RefPtr<DeviceSetPromise> EnumerateRawDevices(
+ dom::MediaSourceEnum aVideoInputType,
+ dom::MediaSourceEnum aAudioInputType, EnumerationFlags aFlags);
+
+ RefPtr<LocalDeviceSetPromise> SelectSettings(
+ const dom::MediaStreamConstraints& aConstraints,
+ dom::CallerType aCallerType, RefPtr<LocalMediaDeviceSetRefCnt> aDevices);
+
+ void GetPref(nsIPrefBranch* aBranch, const char* aPref, const char* aData,
+ int32_t* aVal);
+ void GetPrefBool(nsIPrefBranch* aBranch, const char* aPref, const char* aData,
+ bool* aVal);
+ void GetPrefs(nsIPrefBranch* aBranch, const char* aData);
+
+ // Make private because we want only one instance of this class
+ explicit MediaManager(already_AddRefed<TaskQueue> aMediaThread);
+
+ ~MediaManager() = default;
+ void Shutdown();
+
+ void StopScreensharing(uint64_t aWindowID);
+
+ void RemoveMediaDevicesCallback(uint64_t aWindowID);
+ void DeviceListChanged();
+ void InvalidateDeviceCache();
+ void HandleDeviceListChanged();
+
+ // Returns the number of incomplete tasks associated with this window,
+ // including the newly added task.
+ size_t AddTaskAndGetCount(uint64_t aWindowID, const nsAString& aCallID,
+ RefPtr<GetUserMediaTask> aTask);
+ // Finds the task corresponding to aCallID and removes it from tracking.
+ RefPtr<GetUserMediaTask> TakeGetUserMediaTask(const nsAString& aCallID);
+ // Intended for use with "media.navigator.permission.disabled" to bypass the
+ // permission prompt and use the first appropriate device.
+ void NotifyAllowed(const nsString& aCallID,
+ const LocalMediaDeviceSet& aDevices);
+
+ // Media thread only
+ MediaEngine* GetBackend();
+
+ MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
+
+ // ONLY access from MainThread so we don't need to lock
+ WindowTable mActiveWindows;
+ nsRefPtrHashtable<nsStringHashKey, GetUserMediaTask> mActiveCallbacks;
+ nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mCallIds;
+ nsTArray<RefPtr<dom::GetUserMediaRequest>> mPendingGUMRequest;
+ // non-null if a device enumeration is in progress and was started after the
+ // last device-change invalidation
+ RefPtr<media::Refcountable<nsTArray<MozPromiseHolder<ConstDeviceSetPromise>>>>
+ mPendingDevicesPromises;
+ RefPtr<MediaDeviceSetRefCnt> mPhysicalDevices;
+ TimeStamp mUnhandledDeviceChangeTime;
+ RefPtr<MediaTimer> mDeviceChangeTimer;
+ bool mCamerasMuted = false;
+ bool mMicrophonesMuted = false;
+
+ public:
+ // Always exists
+ const RefPtr<TaskQueue> mMediaThread;
+
+ private:
+ nsCOMPtr<nsIAsyncShutdownBlocker> mShutdownBlocker;
+
+ // ONLY accessed from MediaManagerThread
+ RefPtr<MediaEngine> mBackend;
+
+ // Accessed only on main thread and mMediaThread.
+ // Set before mMediaThread is created, and cleared on main thread after last
+ // mMediaThread task is run.
+ static StaticRefPtr<MediaManager> sSingleton;
+
+ // Connect/Disconnect on media thread only
+ MediaEventListener mDeviceListChangeListener;
+
+ MediaEventProducer<void> mDeviceListChangeEvent;
+
+ public:
+ RefPtr<media::Parent<media::NonE10s>> mNonE10sParent;
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_MEDIAMANAGER_H