dom/media/MediaManager.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     5 #include "MediaEngine.h"
     6 #include "mozilla/Services.h"
     7 #include "mozilla/unused.h"
     8 #include "nsIMediaManager.h"
    10 #include "nsHashKeys.h"
    11 #include "nsGlobalWindow.h"
    12 #include "nsClassHashtable.h"
    13 #include "nsRefPtrHashtable.h"
    14 #include "nsIObserver.h"
    15 #include "nsIPrefService.h"
    16 #include "nsIPrefBranch.h"
    18 #include "nsPIDOMWindow.h"
    19 #include "nsIDOMNavigatorUserMedia.h"
    20 #include "nsXULAppAPI.h"
    21 #include "mozilla/Attributes.h"
    22 #include "mozilla/Preferences.h"
    23 #include "mozilla/StaticPtr.h"
    24 #include "mozilla/dom/MediaStreamBinding.h"
    25 #include "mozilla/dom/MediaStreamTrackBinding.h"
    26 #include "prlog.h"
    27 #include "DOMMediaStream.h"
    29 #ifdef MOZ_WEBRTC
    30 #include "mtransport/runnable_utils.h"
    31 #endif
    33 #ifdef MOZ_WIDGET_GONK
    34 #include "DOMCameraManager.h"
    35 #endif
    37 namespace mozilla {
    38 namespace dom {
    39 class MediaStreamConstraints;
    40 class NavigatorUserMediaSuccessCallback;
    41 class NavigatorUserMediaErrorCallback;
    42 }
    44 #ifdef PR_LOGGING
    45 extern PRLogModuleInfo* GetMediaManagerLog();
    46 #define MM_LOG(msg) PR_LOG(GetMediaManagerLog(), PR_LOG_DEBUG, msg)
    47 #else
    48 #define MM_LOG(msg)
    49 #endif
    51 /**
    52  * This class is an implementation of MediaStreamListener. This is used
    53  * to Start() and Stop() the underlying MediaEngineSource when MediaStreams
    54  * are assigned and deassigned in content.
    55  */
    56 class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
    57 {
    58 public:
    59   // Create in an inactive state
    60   GetUserMediaCallbackMediaStreamListener(nsIThread *aThread,
    61     uint64_t aWindowID)
    62     : mMediaThread(aThread)
    63     , mWindowID(aWindowID)
    64     , mStopped(false)
    65     , mFinished(false)
    66     , mLock("mozilla::GUMCMSL")
    67     , mRemoved(false) {}
    69   ~GetUserMediaCallbackMediaStreamListener()
    70   {
    71     // It's OK to release mStream on any thread; they have thread-safe
    72     // refcounts.
    73   }
    75   void Activate(already_AddRefed<SourceMediaStream> aStream,
    76     MediaEngineSource* aAudioSource,
    77     MediaEngineSource* aVideoSource)
    78   {
    79     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
    80     mStream = aStream;
    81     mAudioSource = aAudioSource;
    82     mVideoSource = aVideoSource;
    83     mLastEndTimeAudio = 0;
    84     mLastEndTimeVideo = 0;
    86     mStream->AddListener(this);
    87   }
    89   MediaStream *Stream() // Can be used to test if Activate was called
    90   {
    91     return mStream;
    92   }
    93   SourceMediaStream *GetSourceStream()
    94   {
    95     NS_ASSERTION(mStream,"Getting stream from never-activated GUMCMSListener");
    96     if (!mStream) {
    97       return nullptr;
    98     }
    99     return mStream->AsSourceStream();
   100   }
   102   // mVideo/AudioSource are set by Activate(), so we assume they're capturing
   103   // if set and represent a real capture device.
   104   bool CapturingVideo()
   105   {
   106     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   107     return mVideoSource && !mStopped &&
   108            (!mVideoSource->IsFake() ||
   109             Preferences::GetBool("media.navigator.permission.fake"));
   110   }
   111   bool CapturingAudio()
   112   {
   113     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   114     return mAudioSource && !mStopped &&
   115            (!mAudioSource->IsFake() ||
   116             Preferences::GetBool("media.navigator.permission.fake"));
   117   }
   119   void SetStopped()
   120   {
   121     mStopped = true;
   122   }
   124   // implement in .cpp to avoid circular dependency with MediaOperationRunnable
   125   // Can be invoked from EITHER MainThread or MSG thread
   126   void Invalidate();
   128   void
   129   AudioConfig(bool aEchoOn, uint32_t aEcho,
   130               bool aAgcOn, uint32_t aAGC,
   131               bool aNoiseOn, uint32_t aNoise,
   132               int32_t aPlayoutDelay)
   133   {
   134     if (mAudioSource) {
   135 #ifdef MOZ_WEBRTC
   136       // Right now these configs are only of use if webrtc is available
   137       RUN_ON_THREAD(mMediaThread,
   138                     WrapRunnable(nsRefPtr<MediaEngineSource>(mAudioSource), // threadsafe
   139                                  &MediaEngineSource::Config,
   140                                  aEchoOn, aEcho, aAgcOn, aAGC, aNoiseOn, aNoise, aPlayoutDelay),
   141                     NS_DISPATCH_NORMAL);
   142 #endif
   143     }
   144   }
   146   void
   147   Remove()
   148   {
   149     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   150     // allow calling even if inactive (!mStream) for easier cleanup
   151     // Caller holds strong reference to us, so no death grip required
   152     MutexAutoLock lock(mLock); // protect access to mRemoved
   153     if (mStream && !mRemoved) {
   154       MM_LOG(("Listener removed on purpose, mFinished = %d", (int) mFinished));
   155       mRemoved = true; // RemoveListener is async, avoid races
   156       // If it's destroyed, don't call - listener will be removed and we'll be notified!
   157       if (!mStream->IsDestroyed()) {
   158         mStream->RemoveListener(this);
   159       }
   160     }
   161   }
   163   // Proxy NotifyPull() to sources
   164   virtual void
   165   NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) MOZ_OVERRIDE
   166   {
   167     // Currently audio sources ignore NotifyPull, but they could
   168     // watch it especially for fake audio.
   169     if (mAudioSource) {
   170       mAudioSource->NotifyPull(aGraph, mStream, kAudioTrack, aDesiredTime, mLastEndTimeAudio);
   171     }
   172     if (mVideoSource) {
   173       mVideoSource->NotifyPull(aGraph, mStream, kVideoTrack, aDesiredTime, mLastEndTimeVideo);
   174     }
   175   }
   177   virtual void
   178   NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
   180   virtual void
   181   NotifyRemoved(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
   183 private:
   184   // Set at construction
   185   nsCOMPtr<nsIThread> mMediaThread;
   186   uint64_t mWindowID;
   188   bool mStopped; // MainThread only
   190   // Set at Activate on MainThread
   192   // Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
   193   // No locking needed as they're only addrefed except on the MediaManager thread
   194   nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe refcnt
   195   nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe refcnt
   196   nsRefPtr<SourceMediaStream> mStream; // threadsafe refcnt
   197   TrackTicks mLastEndTimeAudio;
   198   TrackTicks mLastEndTimeVideo;
   199   bool mFinished;
   201   // Accessed from MainThread and MSG thread
   202   Mutex mLock; // protects mRemoved access from MainThread
   203   bool mRemoved;
   204 };
   206 class GetUserMediaNotificationEvent: public nsRunnable
   207 {
   208   public:
   209     enum GetUserMediaStatus {
   210       STARTING,
   211       STOPPING
   212     };
   213     GetUserMediaNotificationEvent(GetUserMediaCallbackMediaStreamListener* aListener,
   214                                   GetUserMediaStatus aStatus,
   215                                   bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
   216     : mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio)
   217     , mIsVideo(aIsVideo), mWindowID(aWindowID) {}
   219     GetUserMediaNotificationEvent(GetUserMediaStatus aStatus,
   220                                   already_AddRefed<DOMMediaStream> aStream,
   221                                   DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
   222                                   bool aIsAudio, bool aIsVideo, uint64_t aWindowID,
   223                                   already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
   224     : mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback),
   225       mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID),
   226       mError(aError) {}
   227     virtual ~GetUserMediaNotificationEvent()
   228     {
   230     }
   232     NS_IMETHOD Run() MOZ_OVERRIDE;
   234   protected:
   235     nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
   236     nsRefPtr<DOMMediaStream> mStream;
   237     nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
   238     GetUserMediaStatus mStatus;
   239     bool mIsAudio;
   240     bool mIsVideo;
   241     uint64_t mWindowID;
   242     nsRefPtr<nsIDOMGetUserMediaErrorCallback> mError;
   243 };
   245 typedef enum {
   246   MEDIA_START,
   247   MEDIA_STOP
   248 } MediaOperation;
   250 class MediaManager;
   251 class GetUserMediaRunnable;
   253 /**
   254  * Send an error back to content. The error is the form a string.
   255  * Do this only on the main thread. The success callback is also passed here
   256  * so it can be released correctly.
   257  */
   258 class ErrorCallbackRunnable : public nsRunnable
   259 {
   260 public:
   261   ErrorCallbackRunnable(
   262     nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aSuccess,
   263     nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aError,
   264     const nsAString& aErrorMsg, uint64_t aWindowID);
   265   NS_IMETHOD Run();
   266 private:
   267   ~ErrorCallbackRunnable();
   269   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
   270   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
   271   const nsString mErrorMsg;
   272   uint64_t mWindowID;
   273   nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
   274 };
   276 class ReleaseMediaOperationResource : public nsRunnable
   277 {
   278 public:
   279   ReleaseMediaOperationResource(already_AddRefed<DOMMediaStream> aStream,
   280     DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback):
   281     mStream(aStream),
   282     mOnTracksAvailableCallback(aOnTracksAvailableCallback) {}
   283   NS_IMETHOD Run() MOZ_OVERRIDE {return NS_OK;}
   284 private:
   285   nsRefPtr<DOMMediaStream> mStream;
   286   nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
   287 };
   289 // Generic class for running long media operations like Start off the main
   290 // thread, and then (because nsDOMMediaStreams aren't threadsafe),
   291 // ProxyReleases mStream since it's cycle collected.
   292 class MediaOperationRunnable : public nsRunnable
   293 {
   294 public:
   295   // so we can send Stop without AddRef()ing from the MSG thread
   296   MediaOperationRunnable(MediaOperation aType,
   297     GetUserMediaCallbackMediaStreamListener* aListener,
   298     DOMMediaStream* aStream,
   299     DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
   300     MediaEngineSource* aAudioSource,
   301     MediaEngineSource* aVideoSource,
   302     bool aNeedsFinish,
   303     uint64_t aWindowID,
   304     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
   305     : mType(aType)
   306     , mStream(aStream)
   307     , mOnTracksAvailableCallback(aOnTracksAvailableCallback)
   308     , mAudioSource(aAudioSource)
   309     , mVideoSource(aVideoSource)
   310     , mListener(aListener)
   311     , mFinish(aNeedsFinish)
   312     , mWindowID(aWindowID)
   313     , mError(aError)
   314   {}
   316   ~MediaOperationRunnable()
   317   {
   318     // MediaStreams can be released on any thread.
   319   }
   321   nsresult returnAndCallbackError(nsresult rv, const char* errorLog)
   322   {
   323     MM_LOG(("%s , rv=%d", errorLog, rv));
   324     NS_DispatchToMainThread(new ReleaseMediaOperationResource(mStream.forget(),
   325           mOnTracksAvailableCallback.forget()));
   326     nsString log;
   328     log.AssignASCII(errorLog, strlen(errorLog));
   329     nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success;
   330     NS_DispatchToMainThread(new ErrorCallbackRunnable(success, mError,
   331       log, mWindowID));
   332     return NS_OK;
   333   }
   335   NS_IMETHOD
   336   Run() MOZ_OVERRIDE
   337   {
   338     SourceMediaStream *source = mListener->GetSourceStream();
   339     // No locking between these is required as all the callbacks for the
   340     // same MediaStream will occur on the same thread.
   341     if (!source) // means the stream was never Activated()
   342       return NS_OK;
   344     switch (mType) {
   345       case MEDIA_START:
   346         {
   347           NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
   348           nsresult rv;
   350           source->SetPullEnabled(true);
   352           DOMMediaStream::TrackTypeHints expectedTracks = 0;
   353           if (mAudioSource) {
   354             rv = mAudioSource->Start(source, kAudioTrack);
   355             if (NS_SUCCEEDED(rv)) {
   356               expectedTracks |= DOMMediaStream::HINT_CONTENTS_AUDIO;
   357             } else {
   358               return returnAndCallbackError(rv, "Starting audio failed");
   359             }
   360           }
   361           if (mVideoSource) {
   362             rv = mVideoSource->Start(source, kVideoTrack);
   363             if (NS_SUCCEEDED(rv)) {
   364               expectedTracks |= DOMMediaStream::HINT_CONTENTS_VIDEO;
   365             } else {
   366               return returnAndCallbackError(rv, "Starting video failed");
   367             }
   368           }
   370           mOnTracksAvailableCallback->SetExpectedTracks(expectedTracks);
   372           MM_LOG(("started all sources"));
   373           // Forward mOnTracksAvailableCallback to GetUserMediaNotificationEvent,
   374           // because mOnTracksAvailableCallback needs to be added to mStream
   375           // on the main thread.
   376           nsIRunnable *event =
   377             new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STARTING,
   378                                               mStream.forget(),
   379                                               mOnTracksAvailableCallback.forget(),
   380                                               mAudioSource != nullptr,
   381                                               mVideoSource != nullptr,
   382                                               mWindowID, mError.forget());
   383           // event must always be released on mainthread due to the JS callbacks
   384           // in the TracksAvailableCallback
   385           NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
   386         }
   387         break;
   389       case MEDIA_STOP:
   390         {
   391           NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
   392           if (mAudioSource) {
   393             mAudioSource->Stop(source, kAudioTrack);
   394             mAudioSource->Deallocate();
   395           }
   396           if (mVideoSource) {
   397             mVideoSource->Stop(source, kVideoTrack);
   398             mVideoSource->Deallocate();
   399           }
   400           // Do this after stopping all tracks with EndTrack()
   401           if (mFinish) {
   402             source->Finish();
   403           }
   404           nsIRunnable *event =
   405             new GetUserMediaNotificationEvent(mListener,
   406                                               GetUserMediaNotificationEvent::STOPPING,
   407                                               mAudioSource != nullptr,
   408                                               mVideoSource != nullptr,
   409                                               mWindowID);
   410           // event must always be released on mainthread due to the JS callbacks
   411           // in the TracksAvailableCallback
   412           NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
   413         }
   414         break;
   416       default:
   417         MOZ_ASSERT(false,"invalid MediaManager operation");
   418         break;
   419     }
   420     return NS_OK;
   421   }
   423 private:
   424   MediaOperation mType;
   425   nsRefPtr<DOMMediaStream> mStream;
   426   nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
   427   nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe
   428   nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe
   429   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
   430   bool mFinish;
   431   uint64_t mWindowID;
   432   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
   433 };
   435 typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
   436 typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
   438 class MediaDevice : public nsIMediaDevice
   439 {
   440 public:
   441   NS_DECL_THREADSAFE_ISUPPORTS
   442   NS_DECL_NSIMEDIADEVICE
   444   static MediaDevice* Create(MediaEngineVideoSource* source);
   445   static MediaDevice* Create(MediaEngineAudioSource* source);
   447   virtual ~MediaDevice() {}
   448 protected:
   449   MediaDevice(MediaEngineSource* aSource);
   450   nsString mName;
   451   nsString mID;
   452   bool mHasFacingMode;
   453   dom::VideoFacingModeEnum mFacingMode;
   454   nsRefPtr<MediaEngineSource> mSource;
   455 };
   457 class VideoDevice : public MediaDevice
   458 {
   459 public:
   460   VideoDevice(MediaEngineVideoSource* aSource);
   461   NS_IMETHOD GetType(nsAString& aType);
   462   MediaEngineVideoSource* GetSource();
   463 };
   465 class AudioDevice : public MediaDevice
   466 {
   467 public:
   468   AudioDevice(MediaEngineAudioSource* aSource);
   469   NS_IMETHOD GetType(nsAString& aType);
   470   MediaEngineAudioSource* GetSource();
   471 };
   473 // we could add MediaManager if needed
   474 typedef void (*WindowListenerCallback)(MediaManager *aThis,
   475                                        uint64_t aWindowID,
   476                                        StreamListeners *aListeners,
   477                                        void *aData);
   479 class MediaManager MOZ_FINAL : public nsIMediaManagerService,
   480                                public nsIObserver
   481 {
   482 public:
   483   static already_AddRefed<MediaManager> GetInstance();
   485   // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
   486   // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
   487   // from MediaManager thread.
   488   static MediaManager* Get();
   490   static bool Exists()
   491   {
   492     return !!sSingleton;
   493   }
   495   static nsIThread* GetThread() {
   496     return Get()->mMediaThread;
   497   }
   499   static nsresult NotifyRecordingStatusChange(nsPIDOMWindow* aWindow,
   500                                               const nsString& aMsg,
   501                                               const bool& aIsAudio,
   502                                               const bool& aIsVideo);
   504   NS_DECL_THREADSAFE_ISUPPORTS
   505   NS_DECL_NSIOBSERVER
   506   NS_DECL_NSIMEDIAMANAGERSERVICE
   508   MediaEngine* GetBackend(uint64_t aWindowId = 0);
   509   StreamListeners *GetWindowListeners(uint64_t aWindowId) {
   510     NS_ASSERTION(NS_IsMainThread(), "Only access windowlist on main thread");
   512     return mActiveWindows.Get(aWindowId);
   513   }
   514   void RemoveWindowID(uint64_t aWindowId) {
   515     mActiveWindows.Remove(aWindowId);
   516   }
   517   bool IsWindowStillActive(uint64_t aWindowId) {
   518     return !!GetWindowListeners(aWindowId);
   519   }
   520   // Note: also calls aListener->Remove(), even if inactive
   521   void RemoveFromWindowList(uint64_t aWindowID,
   522     GetUserMediaCallbackMediaStreamListener *aListener);
   524   nsresult GetUserMedia(bool aPrivileged,
   525     nsPIDOMWindow* aWindow,
   526     const dom::MediaStreamConstraints& aRawConstraints,
   527     nsIDOMGetUserMediaSuccessCallback* onSuccess,
   528     nsIDOMGetUserMediaErrorCallback* onError);
   530   nsresult GetUserMediaDevices(nsPIDOMWindow* aWindow,
   531     const dom::MediaStreamConstraints& aConstraints,
   532     nsIGetUserMediaDevicesSuccessCallback* onSuccess,
   533     nsIDOMGetUserMediaErrorCallback* onError,
   534     uint64_t aInnerWindowID = 0);
   535   void OnNavigation(uint64_t aWindowID);
   537   MediaEnginePrefs mPrefs;
   539 private:
   540   WindowTable *GetActiveWindows() {
   541     NS_ASSERTION(NS_IsMainThread(), "Only access windowlist on main thread");
   542     return &mActiveWindows;
   543   }
   545   void GetPref(nsIPrefBranch *aBranch, const char *aPref,
   546                const char *aData, int32_t *aVal);
   547   void GetPrefBool(nsIPrefBranch *aBranch, const char *aPref,
   548                    const char *aData, bool *aVal);
   549   void GetPrefs(nsIPrefBranch *aBranch, const char *aData);
   551   // Make private because we want only one instance of this class
   552   MediaManager();
   554   ~MediaManager() {}
   556   void IterateWindowListeners(nsPIDOMWindow *aWindow,
   557                               WindowListenerCallback aCallback,
   558                               void *aData);
   560   void StopMediaStreams();
   562   // ONLY access from MainThread so we don't need to lock
   563   WindowTable mActiveWindows;
   564   nsRefPtrHashtable<nsStringHashKey, GetUserMediaRunnable> mActiveCallbacks;
   565   nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mCallIds;
   566   // Always exists
   567   nsCOMPtr<nsIThread> mMediaThread;
   569   Mutex mMutex;
   570   // protected with mMutex:
   571   RefPtr<MediaEngine> mBackend;
   573   static StaticRefPtr<MediaManager> sSingleton;
   575 #ifdef MOZ_B2G_CAMERA
   576   nsRefPtr<nsDOMCameraManager> mCameraManager;
   577 #endif
   578 };
   580 } // namespace mozilla

mercurial