From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- dom/media/MediaManager.h | 422 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 422 insertions(+) create mode 100644 dom/media/MediaManager.h (limited to 'dom/media/MediaManager.h') 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& aAudioDeviceInfo, + const nsString& aRawID); + + static RefPtr CopyWithNewRawGroupId( + const RefPtr& aOther, const nsString& aRawGroupID); + + dom::MediaSourceEnum GetMediaSource() const; + + protected: + ~MediaDevice(); + + public: + const RefPtr mEngine; + const RefPtr 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 aRawDevice, const nsString& aID, + const nsString& aGroupID, const nsString& aName); + + uint32_t GetBestFitnessDistance( + const nsTArray& aConstraintSets, + dom::CallerType aCallerType); + + nsresult Allocate(const dom::MediaTrackConstraints& aConstraints, + const MediaEnginePrefs& aPrefs, uint64_t aWindowId, + const char** aOutBadConstraint); + void SetTrack(const RefPtr& aTrack, + const nsMainThreadPtrHandle& 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 mRawDevice; + const nsString mName; + const nsString mID; + const nsString mGroupID; + + private: + RefPtr mSource; +}; + +typedef nsRefPtrHashtable + WindowTable; + +class MediaManager final : public nsIMediaManagerService, + public nsIMemoryReporter, + public nsIObserver { + friend DeviceListener; + + public: + static already_AddRefed 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 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 + static RefPtr 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* 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 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 aListener); + void RemoveWindowID(uint64_t aWindowId); + void SendPendingGUMRequest(); + bool IsWindowStillActive(uint64_t aWindowId) { + return !!GetWindowListener(aWindowId); + } + bool IsWindowListenerStillActive( + const RefPtr& 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>; + using MediaDeviceSetRefCnt = media::Refcountable; + using LocalMediaDeviceSet = nsTArray>; + using LocalMediaDeviceSetRefCnt = media::Refcountable; + + using StreamPromise = + MozPromise, RefPtr, true>; + using DeviceSetPromise = + MozPromise, RefPtr, true>; + using ConstDeviceSetPromise = MozPromise, + RefPtr, true>; + using LocalDevicePromise = + MozPromise, RefPtr, true>; + using LocalDeviceSetPromise = MozPromise, + RefPtr, true>; + using MgrPromise = MozPromise, true>; + + RefPtr GetUserMedia( + nsPIDOMWindowInner* aWindow, + const dom::MediaStreamConstraints& aConstraints, + dom::CallerType aCallerType); + + RefPtr 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 GetPhysicalDevices(); + + void OnNavigation(uint64_t aWindowID); + void OnCameraMute(bool aMute); + void OnMicrophoneMute(bool aMute); + bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId); + + MediaEventSource& DeviceListChangeEvent() { + return mDeviceListChangeEvent; + } + RefPtr AnonymizeDevices( + nsPIDOMWindowInner* aWindow, RefPtr 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; + RefPtr EnumerateDevicesImpl( + nsPIDOMWindowInner* aWindow, dom::MediaSourceEnum aVideoInputType, + dom::MediaSourceEnum aAudioInputType, EnumerationFlags aFlags); + + RefPtr EnumerateRawDevices( + dom::MediaSourceEnum aVideoInputType, + dom::MediaSourceEnum aAudioInputType, EnumerationFlags aFlags); + + RefPtr SelectSettings( + const dom::MediaStreamConstraints& aConstraints, + dom::CallerType aCallerType, RefPtr 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 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 aTask); + // Finds the task corresponding to aCallID and removes it from tracking. + RefPtr 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 mActiveCallbacks; + nsClassHashtable> mCallIds; + nsTArray> mPendingGUMRequest; + // non-null if a device enumeration is in progress and was started after the + // last device-change invalidation + RefPtr>>> + mPendingDevicesPromises; + RefPtr mPhysicalDevices; + TimeStamp mUnhandledDeviceChangeTime; + RefPtr mDeviceChangeTimer; + bool mCamerasMuted = false; + bool mMicrophonesMuted = false; + + public: + // Always exists + const RefPtr mMediaThread; + + private: + nsCOMPtr mShutdownBlocker; + + // ONLY accessed from MediaManagerThread + RefPtr 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 sSingleton; + + // Connect/Disconnect on media thread only + MediaEventListener mDeviceListChangeListener; + + MediaEventProducer mDeviceListChangeEvent; + + public: + RefPtr> mNonE10sParent; +}; + +} // namespace mozilla + +#endif // MOZILLA_MEDIAMANAGER_H -- cgit v1.2.3