/* 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 "MediaEngine.h" #include "MediaEnginePrefs.h" #include "mozilla/media/DeviceChangeCallback.h" #include "mozilla/dom/GetUserMediaRequest.h" #include "mozilla/Unused.h" #include "nsAutoPtr.h" #include "nsIMediaManager.h" #include "nsHashKeys.h" #include "nsClassHashtable.h" #include "nsRefPtrHashtable.h" #include "nsIObserver.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsIDOMNavigatorUserMedia.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" #ifdef MOZ_WEBRTC # include "mtransport/runnable_utils.h" #endif // Note, these suck in Windows headers, unfortunately. #include "base/thread.h" #include "base/task.h" namespace mozilla { namespace dom { struct MediaStreamConstraints; struct MediaTrackConstraints; struct MediaTrackConstraintSet; enum class CallerType : uint32_t; enum class MediaDeviceKind : uint8_t; } // namespace dom namespace ipc { class PrincipalInfo; } class AllocationHandle; class GetUserMediaTask; class GetUserMediaWindowListener; class MediaManager; class SourceListener; class MediaDevice : public nsIMediaDevice { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIMEDIADEVICE MediaDevice(const RefPtr& aSource, const nsString& aName, const nsString& aID, const nsString& aGroupID, const nsString& aRawID); MediaDevice(const RefPtr& aAudioDeviceInfo, const nsString& aID, const nsString& aGroupID, const nsString& aRawID = NS_LITERAL_STRING("")); MediaDevice(const RefPtr& aOther, const nsString& aID, const nsString& aGroupID, const nsString& aRawID); uint32_t GetBestFitnessDistance( const nsTArray& aConstraintSets, bool aIsChrome); nsresult Allocate(const dom::MediaTrackConstraints& aConstraints, const MediaEnginePrefs& aPrefs, const mozilla::ipc::PrincipalInfo& aPrincipalInfo, const char** aOutBadConstraint); void SetTrack(const RefPtr& aStream, TrackID aTrackID, const PrincipalHandle& aPrincipal); nsresult Start(); nsresult Reconfigure(const dom::MediaTrackConstraints& aConstraints, const MediaEnginePrefs& aPrefs, const char** aOutBadConstraint); nsresult FocusOnSelectedSource(); nsresult Stop(); nsresult Deallocate(); void Pull(const RefPtr& aStream, TrackID aTrackID, StreamTime aEndOfAppendedData, StreamTime aDesiredTime, const PrincipalHandle& aPrincipal); void GetSettings(dom::MediaTrackSettings& aOutSettings) const; dom::MediaSourceEnum GetMediaSource() const; protected: virtual ~MediaDevice() = default; static uint32_t FitnessDistance( nsString aN, const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint); private: static bool StringsContain(const dom::OwningStringOrStringSequence& aStrings, nsString aN); static uint32_t FitnessDistance( nsString aN, const dom::ConstrainDOMStringParameters& aParams); // Assigned on allocation on media thread, then read on the media thread and // graph thread RefPtr mAllocationHandle; public: const RefPtr mSource; const RefPtr mSinkInfo; const dom::MediaDeviceKind mKind; const bool mScary; const nsString mType; const nsString mName; const nsString mID; const nsString mGroupID; const nsString mRawID; }; typedef nsRefPtrHashtable WindowTable; typedef MozPromise, nsresult, true> SinkInfoPromise; class MediaManager final : public nsIMediaManagerService, public nsIObserver, public DeviceChangeCallback { friend SourceListener; public: static already_AddRefed GetInstance(); // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread // from MediaManager thread. static MediaManager* Get(); static MediaManager* GetIfExists(); static void StartupInit(); static void PostTask(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 PostTask(const char* aName, FunctionType&& aFunction); #ifdef DEBUG static bool IsInMediaThread(); #endif static bool Exists() { return !!sSingleton; } static nsresult NotifyRecordingStatusChange(nsPIDOMWindowInner* aWindow); NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIOBSERVER NS_DECL_NSIMEDIAMANAGERSERVICE media::Parent* GetNonE10sParent(); MediaEngine* GetBackend(); 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, GetUserMediaWindowListener* aListener); void RemoveWindowID(uint64_t aWindowId); void SendPendingGUMRequest(); bool IsWindowStillActive(uint64_t aWindowId) { return !!GetWindowListener(aWindowId); } bool IsWindowListenerStillActive(GetUserMediaWindowListener* aListener); // Note: also calls aListener->Remove(), even if inactive void RemoveFromWindowList(uint64_t aWindowID, GetUserMediaWindowListener* aListener); typedef dom::CallbackObjectHolder GetUserMediaSuccessCallback; typedef dom::CallbackObjectHolder GetUserMediaErrorCallback; static void CallOnError(const GetUserMediaErrorCallback* aCallback, dom::MediaStreamError& aError); static void CallOnSuccess(const GetUserMediaSuccessCallback* aCallback, DOMMediaStream& aStream); typedef nsTArray> MediaDeviceSet; typedef media::Refcountable MediaDeviceSetRefCnt; typedef MozPromise, RefPtr, true> StreamPromise; typedef MozPromise, RefPtr, true> DevicesPromise; typedef MozPromise, true> MgrPromise; typedef MozPromise, true> BadConstraintsPromise; RefPtr GetUserMedia( nsPIDOMWindowInner* aWindow, const dom::MediaStreamConstraints& aConstraints, dom::CallerType aCallerType); nsresult GetUserMediaDevices( nsPIDOMWindowInner* aWindow, const dom::MediaStreamConstraints& aConstraints, dom::MozGetUserMediaDevicesSuccessCallback& aOnSuccess, uint64_t aInnerWindowID = 0, const nsAString& aCallID = nsString()); RefPtr EnumerateDevices(nsPIDOMWindowInner* aWindow, dom::CallerType aCallerType); nsresult EnumerateDevices(nsPIDOMWindowInner* aWindow, dom::Promise& aPromise); RefPtr GetDisplayMedia( nsPIDOMWindowInner* aWindow, const dom::DisplayMediaStreamConstraints& aConstraintsPassedIn, dom::CallerType aCallerType); // Get the sink that corresponds to the given device id. // It is resposible to check if an application is // authorized to play audio through the requested device. // The returned promise will be resolved with the device // information if the device id matches one and operation is // allowed. The default device is always allowed. Non default // devices are allowed only in secure context. It is pending to // implement an user authorization model. The promise will be // rejected in the following cases: // NS_ERROR_NOT_AVAILABLE: Device id does not exist. // NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR: // The requested device exists but it is not allowed to be used. // Currently, this happens only on non-default default devices // and non https connections. TODO, authorization model to allow // an application to play audio through the device (Bug 1493982). // NS_ERROR_ABORT: General error. RefPtr GetSinkDevice(nsPIDOMWindowInner* aWindow, const nsString& aDeviceId); void OnNavigation(uint64_t aWindowID); bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId); MediaEnginePrefs mPrefs; virtual int AddDeviceChangeCallback(DeviceChangeCallback* aCallback) override; virtual void OnDeviceChange() override; private: static nsresult GenerateUUID(nsAString& aResult); static nsresult AnonymizeId(nsAString& aId, const nsACString& aOriginKey); public: // TODO: make private once we upgrade to GCC 4.8+ on linux. static void AnonymizeDevices(MediaDeviceSet& aDevices, const nsACString& aOriginKey, const uint64_t aWindowId); static already_AddRefed ToJSArray( MediaDeviceSet& aDevices); static void GuessVideoDeviceGroupIDs(MediaManager::MediaDeviceSet& aDevices); private: enum class DeviceEnumerationType : uint8_t { Normal, // Enumeration should not return loopback or fake devices Fake, // Enumeration should return fake device(s) Loopback /* Enumeration should return loopback device(s) (possibly in addition to normal devices) */ }; RefPtr EnumerateRawDevices( uint64_t aWindowId, dom::MediaSourceEnum aVideoInputType, dom::MediaSourceEnum aAudioInputType, MediaSinkEnum aAudioOutputType, DeviceEnumerationType aVideoInputEnumType, DeviceEnumerationType aAudioInputEnumType, bool aForceNoPermRequest, const RefPtr& aOutDevices); RefPtr EnumerateDevicesImpl( uint64_t aWindowId, dom::MediaSourceEnum aVideoInputType, dom::MediaSourceEnum aAudioInputType, MediaSinkEnum aAudioOutputType, DeviceEnumerationType aVideoInputEnumType, DeviceEnumerationType aAudioInputEnumType, bool aForceNoPermRequest, const RefPtr& aOutDevices); RefPtr SelectSettings( const dom::MediaStreamConstraints& aConstraints, bool aIsChrome, const RefPtr& aSources); 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 MediaManager(); ~MediaManager() {} void Shutdown(); void StopScreensharing(uint64_t aWindowID); /** * Calls aCallback with a GetUserMediaWindowListener argument once for * each window listener associated with aWindow and its child windows. */ template void IterateWindowListeners(nsPIDOMWindowInner* aWindow, const FunctionType& aCallback); void StopMediaStreams(); void RemoveMediaDevicesCallback(uint64_t aWindowID); // ONLY access from MainThread so we don't need to lock WindowTable mActiveWindows; nsRefPtrHashtable mActiveCallbacks; nsClassHashtable> mCallIds; nsTArray> mPendingGUMRequest; // Always exists nsAutoPtr mMediaThread; nsCOMPtr mShutdownBlocker; // ONLY accessed from MediaManagerThread RefPtr mBackend; static StaticRefPtr sSingleton; nsTArray mDeviceIDs; public: RefPtr> mNonE10sParent; }; } // namespace mozilla #endif // MOZILLA_MEDIAMANAGER_H