Bug 1266646 - Move group of MediaStreamListener to a new header file. r=pehrsons
authorctai <ctai@mozilla.com>
Thu, 30 Jun 2016 15:07:48 +0800
changeset 346046 6062a5e09ab773085a16cd0cd91f0cb0c352aa9a
parent 346045 d77a9ed241ef80d8e880618de2d4181c0529123a
child 346047 49295086e189276fdab180f5e907538d929c6f7f
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspehrsons
bugs1266646
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1266646 - Move group of MediaStreamListener to a new header file. r=pehrsons This can reduce the include header dependency. MediaStreamVideoSink will inherit from DirectMediaStreamTrackListener. But we can't use forward declaration on MediaStreamListener because the usage of nsTArray<RefPtr<MediaStreamVideoSink>>. MozReview-Commit-ID: 328s4Kw9NvW
dom/camera/CameraPreviewMediaStream.cpp
dom/camera/DOMCameraControl.cpp
dom/html/HTMLMediaElement.cpp
dom/media/AudioCaptureStream.cpp
dom/media/DOMMediaStream.cpp
dom/media/MediaManager.cpp
dom/media/MediaManager.h
dom/media/MediaStreamGraph.cpp
dom/media/MediaStreamGraph.h
dom/media/MediaStreamListener.cpp
dom/media/MediaStreamListener.h
dom/media/TrackUnionStream.cpp
dom/media/encoder/MediaEncoder.cpp
dom/media/encoder/MediaEncoder.h
dom/media/encoder/TrackEncoder.cpp
dom/media/encoder/TrackEncoder.h
dom/media/gtest/TestVideoTrackEncoder.cpp
dom/media/imagecapture/CaptureTask.h
dom/media/mediasink/DecodedStream.cpp
dom/media/moz.build
dom/media/webaudio/AudioNodeStream.cpp
dom/media/webspeech/recognition/SpeechStreamListener.cpp
dom/media/webspeech/recognition/SpeechStreamListener.h
dom/media/webspeech/synth/nsSpeechTask.cpp
dom/media/webspeech/synth/nsSpeechTask.h
media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
media/webrtc/signaling/test/FakeMediaStreams.h
media/webrtc/signaling/test/FakeMediaStreamsImpl.h
--- a/dom/camera/CameraPreviewMediaStream.cpp
+++ b/dom/camera/CameraPreviewMediaStream.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* 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/. */
 
 #include "CameraPreviewMediaStream.h"
 #include "CameraCommon.h"
+#include "MediaStreamListener.h"
 
 /**
  * Maximum number of outstanding invalidates before we start to drop frames;
  * if we hit this threshold, it is an indicator that the main thread is
  * either very busy or the device is busy elsewhere (e.g. encoding or
  * persisting video data).
  */
 #define MAX_INVALIDATE_PENDING 4
@@ -84,31 +85,31 @@ CameraPreviewMediaStream::AddListener(Me
 
 void
 CameraPreviewMediaStream::RemoveListener(MediaStreamListener* aListener)
 {
   MutexAutoLock lock(mMutex);
 
   RefPtr<MediaStreamListener> listener(aListener);
   mListeners.RemoveElement(aListener);
-  listener->NotifyEvent(mFakeMediaStreamGraph, MediaStreamListener::EVENT_REMOVED);
+  listener->NotifyEvent(mFakeMediaStreamGraph, MediaStreamGraphEvent::EVENT_REMOVED);
 }
 
 void
 CameraPreviewMediaStream::OnPreviewStateChange(bool aActive)
 {
   if (aActive) {
     MutexAutoLock lock(mMutex);
     if (!mTrackCreated) {
       mTrackCreated = true;
       VideoSegment tmpSegment;
       for (uint32_t j = 0; j < mListeners.Length(); ++j) {
         MediaStreamListener* l = mListeners[j];
         l->NotifyQueuedTrackChanges(mFakeMediaStreamGraph, TRACK_VIDEO, 0,
-                                    MediaStreamListener::TRACK_EVENT_CREATED,
+                                    TrackEventCommand::TRACK_EVENT_CREATED,
                                     tmpSegment);
         l->NotifyFinishedTrackCreation(mFakeMediaStreamGraph);
       }
     }
   }
 }
 
 void
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -62,22 +62,22 @@ public:
     if (!mCameraControl) {
       return;
     }
 
     mCameraControl->TrackCreated(aTrackID);
   }
 
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
-                                StreamTime aTrackOffset, uint32_t aTrackEvents,
+                                StreamTime aTrackOffset, TrackEventCommand aTrackEvents,
                                 const MediaSegment& aQueuedMedia,
                                 MediaStream* aInputStream,
                                 TrackID aInputTrackID) override
   {
-    if (aTrackEvents & TRACK_EVENT_CREATED) {
+    if (aTrackEvents & TrackEventCommand::TRACK_EVENT_CREATED) {
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(NewRunnableMethod<TrackID>(
           this, &TrackCreatedListener::DoNotifyTrackCreated, aID));
     }
   }
 
 protected:
   ~TrackCreatedListener() {}
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -64,16 +64,17 @@
 #include "nsURIHashKey.h"
 #include "nsJSUtils.h"
 #include "MediaStreamGraph.h"
 #include "nsIScriptError.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "mozilla/dom/MediaSource.h"
 #include "MediaMetadataManager.h"
 #include "MediaSourceDecoder.h"
+#include "MediaStreamListener.h"
 #include "DOMMediaStream.h"
 #include "AudioStreamTrack.h"
 #include "VideoStreamTrack.h"
 #include "MediaTrackList.h"
 
 #include "AudioChannelService.h"
 
 #include "mozilla/dom/power/PowerManagerService.h"
@@ -3328,19 +3329,19 @@ public:
     if (aBlocked == BLOCKED) {
       event = NewRunnableMethod(this, &StreamListener::DoNotifyBlocked);
     } else {
       event = NewRunnableMethod(this, &StreamListener::DoNotifyUnblocked);
     }
     aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
   }
   virtual void NotifyEvent(MediaStreamGraph* aGraph,
-                           MediaStreamListener::MediaStreamGraphEvent event) override
+                           MediaStreamGraphEvent event) override
   {
-    if (event == EVENT_FINISHED) {
+    if (event == MediaStreamGraphEvent::EVENT_FINISHED) {
       nsCOMPtr<nsIRunnable> event =
         NewRunnableMethod(this, &StreamListener::DoNotifyFinished);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
     }
   }
   virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) override
   {
     MutexAutoLock lock(mMutex);
@@ -3390,17 +3391,17 @@ public:
       return;
     }
     RefPtr<HTMLMediaElement> deathGrip = mElement;
     mElement->UpdateInitialMediaSize(aSize);
   }
 
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                 StreamTime aTrackOffset,
-                                uint32_t aTrackEvents,
+                                TrackEventCommand aTrackEvents,
                                 const MediaSegment& aQueuedMedia,
                                 MediaStream* aInputStream,
                                 TrackID aInputTrackID) override
   {
     if (mInitialSizeFound || aQueuedMedia.GetType() != MediaSegment::VIDEO) {
       return;
     }
     const VideoSegment& video = static_cast<const VideoSegment&>(aQueuedMedia);
--- a/dom/media/AudioCaptureStream.cpp
+++ b/dom/media/AudioCaptureStream.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* 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/. */
 
 #include "MediaStreamGraphImpl.h"
+#include "MediaStreamListener.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/unused.h"
 
 #include "AudioSegment.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Attributes.h"
 #include "AudioCaptureStream.h"
 #include "ImageContainer.h"
@@ -73,17 +74,17 @@ AudioCaptureStream::ProcessInput(GraphTi
   uint32_t inputCount = mInputs.Length();
   StreamTracks::Track* track = EnsureTrack(mTrackId);
   // Notify the DOM everything is in order.
   if (!mTrackCreated) {
     for (uint32_t i = 0; i < mListeners.Length(); i++) {
       MediaStreamListener* l = mListeners[i];
       AudioSegment tmp;
       l->NotifyQueuedTrackChanges(
-        Graph(), mTrackId, 0, MediaStreamListener::TRACK_EVENT_CREATED, tmp);
+        Graph(), mTrackId, 0, TrackEventCommand::TRACK_EVENT_CREATED, tmp);
       l->NotifyFinishedTrackCreation(Graph());
     }
     mTrackCreated = true;
   }
 
   if (IsFinishedOnGraphThread()) {
     return;
   }
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -181,28 +181,28 @@ public:
     if (track) {
       LOG(LogLevel::Debug, ("DOMMediaStream %p MediaStreamTrack %p ended at the source. Marking it ended.",
                             mStream, track.get()));
       track->NotifyEnded();
     }
   }
 
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
-                                StreamTime aTrackOffset, uint32_t aTrackEvents,
+                                StreamTime aTrackOffset, TrackEventCommand aTrackEvents,
                                 const MediaSegment& aQueuedMedia,
                                 MediaStream* aInputStream,
                                 TrackID aInputTrackID) override
   {
-    if (aTrackEvents & TRACK_EVENT_CREATED) {
+    if (aTrackEvents & TrackEventCommand::TRACK_EVENT_CREATED) {
       nsCOMPtr<nsIRunnable> runnable =
         NewRunnableMethod<TrackID, MediaSegment::Type, MediaStream*, TrackID>(
           this, &OwnedStreamListener::DoNotifyTrackCreated,
           aID, aQueuedMedia.GetType(), aInputStream, aInputTrackID);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
-    } else if (aTrackEvents & TRACK_EVENT_ENDED) {
+    } else if (aTrackEvents & TrackEventCommand::TRACK_EVENT_ENDED) {
       nsCOMPtr<nsIRunnable> runnable =
         NewRunnableMethod<MediaStream*, TrackID, TrackID>(
           this, &OwnedStreamListener::DoNotifyTrackEnded,
           aInputStream, aInputTrackID, aID);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
     }
   }
 
@@ -271,22 +271,22 @@ public:
     }
 
     mStream->NotifyTracksCreated();
   }
 
   // The methods below are called on the MediaStreamGraph thread.
 
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
-                                StreamTime aTrackOffset, uint32_t aTrackEvents,
+                                StreamTime aTrackOffset, TrackEventCommand aTrackEvents,
                                 const MediaSegment& aQueuedMedia,
                                 MediaStream* aInputStream,
                                 TrackID aInputTrackID) override
   {
-    if (aTrackEvents & TRACK_EVENT_ENDED) {
+    if (aTrackEvents & TrackEventCommand::TRACK_EVENT_ENDED) {
       nsCOMPtr<nsIRunnable> runnable =
         NewRunnableMethod<StorensRefPtrPassByPtr<MediaStream>, TrackID>(
           this, &PlaybackStreamListener::DoNotifyTrackEnded, aInputStream, aInputTrackID);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
     }
   }
 
   void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) override
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -4,16 +4,17 @@
  * 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/. */
 
 #include "MediaManager.h"
 
 #include "MediaStreamGraph.h"
 #include "mozilla/dom/MediaStreamTrack.h"
 #include "GetUserMediaRequest.h"
+#include "MediaStreamListener.h"
 #include "nsContentUtils.h"
 #include "nsHashPropertyBag.h"
 #ifdef MOZ_WIDGET_GONK
 #include "nsIAudioManager.h"
 #endif
 #include "nsIEventTarget.h"
 #include "nsIUUIDGenerator.h"
 #include "nsIScriptGlobalObject.h"
@@ -238,16 +239,263 @@ HostHasPermission(nsIURI &docURI)
     }
 
     begin = end + 1;
   } while (end < domainWhiteList.Length());
 
   return false;
 }
 
+/**
+ * This class is an implementation of MediaStreamListener. This is used
+ * to Start() and Stop() the underlying MediaEngineSource when MediaStreams
+ * are assigned and deassigned in content.
+ */
+class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
+{
+public:
+  // Create in an inactive state
+  GetUserMediaCallbackMediaStreamListener(base::Thread *aThread,
+    uint64_t aWindowID,
+    const PrincipalHandle& aPrincipalHandle)
+    : mMediaThread(aThread)
+    , mMainThreadCheck(nullptr)
+    , mWindowID(aWindowID)
+    , mPrincipalHandle(aPrincipalHandle)
+    , mStopped(false)
+    , mFinished(false)
+    , mRemoved(false)
+    , mAudioStopped(false)
+    , mVideoStopped(false) {}
+
+  ~GetUserMediaCallbackMediaStreamListener()
+  {
+    Unused << mMediaThread;
+    // It's OK to release mStream on any thread; they have thread-safe
+    // refcounts.
+  }
+
+  void Activate(already_AddRefed<SourceMediaStream> aStream,
+                AudioDevice* aAudioDevice,
+                VideoDevice* aVideoDevice)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mMainThreadCheck = PR_GetCurrentThread();
+    mStream = aStream;
+    mAudioDevice = aAudioDevice;
+    mVideoDevice = aVideoDevice;
+
+    mStream->AddListener(this);
+  }
+
+  MediaStream *Stream() // Can be used to test if Activate was called
+  {
+    return mStream;
+  }
+  SourceMediaStream *GetSourceStream()
+  {
+    NS_ASSERTION(mStream,"Getting stream from never-activated GUMCMSListener");
+    if (!mStream) {
+      return nullptr;
+    }
+    return mStream->AsSourceStream();
+  }
+
+  void StopSharing();
+
+  void StopTrack(TrackID aID);
+
+  typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
+
+  already_AddRefed<PledgeVoid>
+  ApplyConstraintsToTrack(nsPIDOMWindowInner* aWindow,
+                          TrackID aID,
+                          const dom::MediaTrackConstraints& aConstraints);
+
+  // mVideo/AudioDevice are set by Activate(), so we assume they're capturing
+  // if set and represent a real capture device.
+  bool CapturingVideo()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mVideoDevice && !mStopped &&
+           !mVideoDevice->GetSource()->IsAvailable() &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera &&
+           (!mVideoDevice->GetSource()->IsFake() ||
+            Preferences::GetBool("media.navigator.permission.fake"));
+  }
+  bool CapturingAudio()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mAudioDevice && !mStopped &&
+           !mAudioDevice->GetSource()->IsAvailable() &&
+           (!mAudioDevice->GetSource()->IsFake() ||
+            Preferences::GetBool("media.navigator.permission.fake"));
+  }
+  bool CapturingScreen()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mVideoDevice && !mStopped &&
+           !mVideoDevice->GetSource()->IsAvailable() &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen;
+  }
+  bool CapturingWindow()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mVideoDevice && !mStopped &&
+           !mVideoDevice->GetSource()->IsAvailable() &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window;
+  }
+  bool CapturingApplication()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mVideoDevice && !mStopped &&
+           !mVideoDevice->GetSource()->IsAvailable() &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application;
+  }
+  bool CapturingBrowser()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mVideoDevice && !mStopped &&
+           mVideoDevice->GetSource()->IsAvailable() &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
+  }
+
+  // implement in .cpp to avoid circular dependency with MediaOperationTask
+  // Can be invoked from EITHER MainThread or MSG thread
+  void Stop();
+
+  void
+  AudioConfig(bool aEchoOn, uint32_t aEcho,
+              bool aAgcOn, uint32_t aAGC,
+              bool aNoiseOn, uint32_t aNoise,
+              int32_t aPlayoutDelay);
+
+  void
+  Remove()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    // allow calling even if inactive (!mStream) for easier cleanup
+    // Caller holds strong reference to us, so no death grip required
+    if (mStream && !mRemoved) {
+      MM_LOG(("Listener removed on purpose, mFinished = %d", (int) mFinished));
+      mRemoved = true; // RemoveListener is async, avoid races
+      // If it's destroyed, don't call - listener will be removed and we'll be notified!
+      if (!mStream->IsDestroyed()) {
+        mStream->RemoveListener(this);
+      }
+    }
+  }
+
+  // Proxy NotifyPull() to sources
+  void
+  NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) override
+  {
+    // Currently audio sources ignore NotifyPull, but they could
+    // watch it especially for fake audio.
+    if (mAudioDevice) {
+      mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack,
+                                            aDesiredTime, mPrincipalHandle);
+    }
+    if (mVideoDevice) {
+      mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack,
+                                            aDesiredTime, mPrincipalHandle);
+    }
+  }
+
+  void
+  NotifyEvent(MediaStreamGraph* aGraph,
+              MediaStreamGraphEvent aEvent) override
+  {
+    nsresult rv;
+    nsCOMPtr<nsIThread> thread;
+
+    switch (aEvent) {
+      case MediaStreamGraphEvent::EVENT_FINISHED:
+        rv = NS_GetMainThread(getter_AddRefs(thread));
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          NS_ASSERTION(false, "Mainthread not available; running on current thread");
+          // Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
+          MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
+          NotifyFinished();
+          return;
+        }
+        thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyFinished),
+                         NS_DISPATCH_NORMAL);
+        break;
+      case MediaStreamGraphEvent::EVENT_REMOVED:
+        rv = NS_GetMainThread(getter_AddRefs(thread));
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          NS_ASSERTION(false, "Mainthread not available; running on current thread");
+          // Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
+          MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
+          NotifyRemoved();
+          return;
+        }
+        thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyRemoved),
+                         NS_DISPATCH_NORMAL);
+        break;
+      case MediaStreamGraphEvent::EVENT_HAS_DIRECT_LISTENERS:
+        NotifyDirectListeners(aGraph, true);
+        break;
+      case MediaStreamGraphEvent::EVENT_HAS_NO_DIRECT_LISTENERS:
+        NotifyDirectListeners(aGraph, false);
+        break;
+      default:
+        break;
+    }
+  }
+
+  void
+  NotifyFinished();
+
+  void
+  NotifyRemoved();
+
+  void
+  NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners);
+
+  PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; }
+
+private:
+  // Set at construction
+  base::Thread* mMediaThread;
+  // never ever indirect off this; just for assertions
+  PRThread* mMainThreadCheck;
+
+  uint64_t mWindowID;
+  const PrincipalHandle mPrincipalHandle;
+
+  // true after this listener has sent MEDIA_STOP. MainThread only.
+  bool mStopped;
+
+  // true after the stream this listener is listening to has finished in the
+  // MediaStreamGraph. MainThread only.
+  bool mFinished;
+
+  // true after this listener has been removed from its MediaStream.
+  // MainThread only.
+  bool mRemoved;
+
+  // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice.
+  // MainThread only.
+  bool mAudioStopped;
+
+  // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mVideoDevice.
+  // MainThread only.
+  bool mVideoStopped;
+
+  // Set at Activate on MainThread
+
+  // Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
+  // No locking needed as they're only addrefed except on the MediaManager thread
+  RefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt
+  RefPtr<VideoDevice> mVideoDevice; // threadsafe refcnt
+  RefPtr<SourceMediaStream> mStream; // threadsafe refcnt
+};
+
 // Generic class for running long media operations like Start off the main
 // thread, and then (because nsDOMMediaStreams aren't threadsafe),
 // ProxyReleases mStream since it's cycle collected.
 class MediaOperationTask : public Runnable
 {
 public:
   // so we can send Stop without AddRef()ing from the MSG thread
   MediaOperationTask(MediaOperation aType,
@@ -3139,24 +3387,24 @@ GetUserMediaCallbackMediaStreamListener:
                                     dom::AudioChannel::Normal);
     graph->UnregisterCaptureStreamForWindow(mWindowID);
     mStream->Destroy();
   }
 }
 
 // ApplyConstraints for track
 
-already_AddRefed<GetUserMediaCallbackMediaStreamListener::PledgeVoid>
+already_AddRefed<media::Pledge<bool, dom::MediaStreamError*>>
 GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack(
     nsPIDOMWindowInner* aWindow,
     TrackID aTrackID,
     const MediaTrackConstraints& aConstraints)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  RefPtr<PledgeVoid> p = new PledgeVoid();
+  RefPtr<media::Pledge<bool, dom::MediaStreamError*>> p = new media::Pledge<bool, dom::MediaStreamError*>();
 
   // XXX to support multiple tracks of a type in a stream, this should key off
   // the TrackID and not just the type
   RefPtr<AudioDevice> audioDevice =
     aTrackID == kAudioTrack ? mAudioDevice.get() : nullptr;
   RefPtr<VideoDevice> videoDevice =
     aTrackID == kVideoTrack ? mVideoDevice.get() : nullptr;
 
@@ -3199,17 +3447,17 @@ GetUserMediaCallbackMediaStreamListener:
     }
     NS_DispatchToMainThread(NewRunnableFrom([id, windowId, rv,
                                              badConstraint]() mutable {
       MOZ_ASSERT(NS_IsMainThread());
       RefPtr<MediaManager> mgr = MediaManager_GetInstance();
       if (!mgr) {
         return NS_OK;
       }
-      RefPtr<PledgeVoid> p = mgr->mOutstandingVoidPledges.Remove(id);
+      RefPtr<media::Pledge<bool, dom::MediaStreamError*>> p = mgr->mOutstandingVoidPledges.Remove(id);
       if (p) {
         if (NS_SUCCEEDED(rv)) {
           p->Resolve(false);
         } else {
           auto* window = nsGlobalWindow::GetInnerWindowWithId(windowId);
           if (window) {
             if (rv == NS_ERROR_NOT_AVAILABLE) {
               nsString constraint;
@@ -3315,16 +3563,36 @@ GetUserMediaCallbackMediaStreamListener:
   MM_LOG(("Listener removed by DOM Destroy(), mFinished = %d", (int) mFinished));
   mRemoved = true;
 
   if (!mFinished) {
     NotifyFinished();
   }
 }
 
+GetUserMediaNotificationEvent::GetUserMediaNotificationEvent(
+    GetUserMediaCallbackMediaStreamListener* aListener,
+    GetUserMediaStatus aStatus,
+    bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
+: mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio)
+, mIsVideo(aIsVideo), mWindowID(aWindowID) {}
+
+GetUserMediaNotificationEvent::GetUserMediaNotificationEvent(
+    GetUserMediaStatus aStatus,
+    already_AddRefed<DOMMediaStream> aStream,
+    OnTracksAvailableCallback* aOnTracksAvailableCallback,
+    bool aIsAudio, bool aIsVideo, uint64_t aWindowID,
+    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
+: mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback),
+  mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID),
+  mOnFailure(aError) {}
+GetUserMediaNotificationEvent::~GetUserMediaNotificationEvent()
+{
+}
+
 NS_IMETHODIMP
 GetUserMediaNotificationEvent::Run()
 {
   MOZ_ASSERT(NS_IsMainThread());
   // Make sure mStream is cleared and our reference to the DOMMediaStream
   // is dropped on the main thread, no matter what happens in this method.
   // Otherwise this object might be destroyed off the main thread,
   // releasing DOMMediaStream off the main thread, which is not allowed.
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -48,16 +48,20 @@
 
 namespace mozilla {
 namespace dom {
 struct MediaStreamConstraints;
 struct MediaTrackConstraints;
 struct MediaTrackConstraintSet;
 } // namespace dom
 
+class MediaManager;
+class GetUserMediaCallbackMediaStreamListener;
+class GetUserMediaTask;
+
 extern LogModule* GetMediaManagerLog();
 #define MM_LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
 
 class MediaDevice : public nsIMediaDevice
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEDIADEVICE
@@ -112,289 +116,34 @@ public:
   Source* GetSource();
   nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                     const MediaEnginePrefs &aPrefs,
                     const nsACString& aOrigin);
   nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
                    const MediaEnginePrefs &aPrefs);
 };
 
-/**
- * This class is an implementation of MediaStreamListener. This is used
- * to Start() and Stop() the underlying MediaEngineSource when MediaStreams
- * are assigned and deassigned in content.
- */
-class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
-{
-public:
-  // Create in an inactive state
-  GetUserMediaCallbackMediaStreamListener(base::Thread *aThread,
-    uint64_t aWindowID,
-    const PrincipalHandle& aPrincipalHandle)
-    : mMediaThread(aThread)
-    , mMainThreadCheck(nullptr)
-    , mWindowID(aWindowID)
-    , mPrincipalHandle(aPrincipalHandle)
-    , mStopped(false)
-    , mFinished(false)
-    , mRemoved(false)
-    , mAudioStopped(false)
-    , mVideoStopped(false) {}
-
-  ~GetUserMediaCallbackMediaStreamListener()
-  {
-    Unused << mMediaThread;
-    // It's OK to release mStream on any thread; they have thread-safe
-    // refcounts.
-  }
-
-  void Activate(already_AddRefed<SourceMediaStream> aStream,
-                AudioDevice* aAudioDevice,
-                VideoDevice* aVideoDevice)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    mMainThreadCheck = PR_GetCurrentThread();
-    mStream = aStream;
-    mAudioDevice = aAudioDevice;
-    mVideoDevice = aVideoDevice;
-
-    mStream->AddListener(this);
-  }
-
-  MediaStream *Stream() // Can be used to test if Activate was called
-  {
-    return mStream;
-  }
-  SourceMediaStream *GetSourceStream()
-  {
-    NS_ASSERTION(mStream,"Getting stream from never-activated GUMCMSListener");
-    if (!mStream) {
-      return nullptr;
-    }
-    return mStream->AsSourceStream();
-  }
-
-  void StopSharing();
-
-  void StopTrack(TrackID aID);
-
-  typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
-
-  already_AddRefed<PledgeVoid>
-  ApplyConstraintsToTrack(nsPIDOMWindowInner* aWindow,
-                          TrackID aID,
-                          const dom::MediaTrackConstraints& aConstraints);
-
-  // mVideo/AudioDevice are set by Activate(), so we assume they're capturing
-  // if set and represent a real capture device.
-  bool CapturingVideo()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    return mVideoDevice && !mStopped &&
-           !mVideoDevice->GetSource()->IsAvailable() &&
-           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera &&
-           (!mVideoDevice->GetSource()->IsFake() ||
-            Preferences::GetBool("media.navigator.permission.fake"));
-  }
-  bool CapturingAudio()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    return mAudioDevice && !mStopped &&
-           !mAudioDevice->GetSource()->IsAvailable() &&
-           (!mAudioDevice->GetSource()->IsFake() ||
-            Preferences::GetBool("media.navigator.permission.fake"));
-  }
-  bool CapturingScreen()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    return mVideoDevice && !mStopped &&
-           !mVideoDevice->GetSource()->IsAvailable() &&
-           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen;
-  }
-  bool CapturingWindow()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    return mVideoDevice && !mStopped &&
-           !mVideoDevice->GetSource()->IsAvailable() &&
-           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window;
-  }
-  bool CapturingApplication()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    return mVideoDevice && !mStopped &&
-           !mVideoDevice->GetSource()->IsAvailable() &&
-           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application;
-  }
-  bool CapturingBrowser()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    return mVideoDevice && !mStopped &&
-           mVideoDevice->GetSource()->IsAvailable() &&
-           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
-  }
-
-  // implement in .cpp to avoid circular dependency with MediaOperationTask
-  // Can be invoked from EITHER MainThread or MSG thread
-  void Stop();
-
-  void
-  AudioConfig(bool aEchoOn, uint32_t aEcho,
-              bool aAgcOn, uint32_t aAGC,
-              bool aNoiseOn, uint32_t aNoise,
-              int32_t aPlayoutDelay);
-
-  void
-  Remove()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    // allow calling even if inactive (!mStream) for easier cleanup
-    // Caller holds strong reference to us, so no death grip required
-    if (mStream && !mRemoved) {
-      MM_LOG(("Listener removed on purpose, mFinished = %d", (int) mFinished));
-      mRemoved = true; // RemoveListener is async, avoid races
-      // If it's destroyed, don't call - listener will be removed and we'll be notified!
-      if (!mStream->IsDestroyed()) {
-        mStream->RemoveListener(this);
-      }
-    }
-  }
-
-  // Proxy NotifyPull() to sources
-  void
-  NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) override
-  {
-    // Currently audio sources ignore NotifyPull, but they could
-    // watch it especially for fake audio.
-    if (mAudioDevice) {
-      mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack,
-                                            aDesiredTime, mPrincipalHandle);
-    }
-    if (mVideoDevice) {
-      mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack,
-                                            aDesiredTime, mPrincipalHandle);
-    }
-  }
-
-  void
-  NotifyEvent(MediaStreamGraph* aGraph,
-              MediaStreamListener::MediaStreamGraphEvent aEvent) override
-  {
-    nsresult rv;
-    nsCOMPtr<nsIThread> thread;
-
-    switch (aEvent) {
-      case EVENT_FINISHED:
-        rv = NS_GetMainThread(getter_AddRefs(thread));
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          NS_ASSERTION(false, "Mainthread not available; running on current thread");
-          // Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
-          MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
-          NotifyFinished();
-          return;
-        }
-        thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyFinished),
-                         NS_DISPATCH_NORMAL);
-        break;
-      case EVENT_REMOVED:
-        rv = NS_GetMainThread(getter_AddRefs(thread));
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          NS_ASSERTION(false, "Mainthread not available; running on current thread");
-          // Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
-          MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
-          NotifyRemoved();
-          return;
-        }
-        thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyRemoved),
-                         NS_DISPATCH_NORMAL);
-        break;
-      case EVENT_HAS_DIRECT_LISTENERS:
-        NotifyDirectListeners(aGraph, true);
-        break;
-      case EVENT_HAS_NO_DIRECT_LISTENERS:
-        NotifyDirectListeners(aGraph, false);
-        break;
-      default:
-        break;
-    }
-  }
-
-  void
-  NotifyFinished();
-
-  void
-  NotifyRemoved();
-
-  void
-  NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners);
-
-  PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; }
-
-private:
-  // Set at construction
-  base::Thread* mMediaThread;
-  // never ever indirect off this; just for assertions
-  PRThread* mMainThreadCheck;
-
-  uint64_t mWindowID;
-  const PrincipalHandle mPrincipalHandle;
-
-  // true after this listener has sent MEDIA_STOP. MainThread only.
-  bool mStopped;
-
-  // true after the stream this listener is listening to has finished in the
-  // MediaStreamGraph. MainThread only.
-  bool mFinished;
-
-  // true after this listener has been removed from its MediaStream.
-  // MainThread only.
-  bool mRemoved;
-
-  // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice.
-  // MainThread only.
-  bool mAudioStopped;
-
-  // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mVideoDevice.
-  // MainThread only.
-  bool mVideoStopped;
-
-  // Set at Activate on MainThread
-
-  // Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
-  // No locking needed as they're only addrefed except on the MediaManager thread
-  RefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt
-  RefPtr<VideoDevice> mVideoDevice; // threadsafe refcnt
-  RefPtr<SourceMediaStream> mStream; // threadsafe refcnt
-};
-
 class GetUserMediaNotificationEvent: public Runnable
 {
   public:
     enum GetUserMediaStatus {
       STARTING,
       STOPPING,
       STOPPED_TRACK,
     };
     GetUserMediaNotificationEvent(GetUserMediaCallbackMediaStreamListener* aListener,
                                   GetUserMediaStatus aStatus,
-                                  bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
-    : mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio)
-    , mIsVideo(aIsVideo), mWindowID(aWindowID) {}
+                                  bool aIsAudio, bool aIsVideo, uint64_t aWindowID);
 
     GetUserMediaNotificationEvent(GetUserMediaStatus aStatus,
                                   already_AddRefed<DOMMediaStream> aStream,
                                   OnTracksAvailableCallback* aOnTracksAvailableCallback,
                                   bool aIsAudio, bool aIsVideo, uint64_t aWindowID,
-                                  already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
-    : mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback),
-      mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID),
-      mOnFailure(aError) {}
-    virtual ~GetUserMediaNotificationEvent()
-    {
-
-    }
+                                  already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError);
+    virtual ~GetUserMediaNotificationEvent();
 
     NS_IMETHOD Run() override;
 
   protected:
     RefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
     RefPtr<DOMMediaStream> mStream;
     nsAutoPtr<OnTracksAvailableCallback> mOnTracksAvailableCallback;
     GetUserMediaStatus mStatus;
@@ -406,19 +155,16 @@ class GetUserMediaNotificationEvent: pub
 
 typedef enum {
   MEDIA_START,
   MEDIA_STOP,
   MEDIA_STOP_TRACK,
   MEDIA_DIRECT_LISTENERS,
 } MediaOperation;
 
-class MediaManager;
-class GetUserMediaTask;
-
 class ReleaseMediaOperationResource : public Runnable
 {
 public:
   ReleaseMediaOperationResource(already_AddRefed<DOMMediaStream> aStream,
     OnTracksAvailableCallback* aOnTracksAvailableCallback):
     mStream(aStream),
     mOnTracksAvailableCallback(aOnTracksAvailableCallback) {}
   NS_IMETHOD Run() override {return NS_OK;}
@@ -570,17 +316,17 @@ private:
 
   // ONLY accessed from MediaManagerThread
   RefPtr<MediaEngine> mBackend;
 
   static StaticRefPtr<MediaManager> sSingleton;
 
   media::CoatCheck<PledgeSourceSet> mOutstandingPledges;
   media::CoatCheck<PledgeChar> mOutstandingCharPledges;
-  media::CoatCheck<GetUserMediaCallbackMediaStreamListener::PledgeVoid> mOutstandingVoidPledges;
+  media::CoatCheck<media::Pledge<bool, dom::MediaStreamError*>> mOutstandingVoidPledges;
 #if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   RefPtr<nsDOMCameraManager> mCameraManager;
 #endif
 public:
   media::CoatCheck<media::Pledge<nsCString>> mGetOriginKeyPledges;
   UniquePtr<media::Parent<media::NonE10s>> mNonE10sParent;
 };
 
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/Logging.h"
 #include "mozilla/Attributes.h"
 #include "TrackUnionStream.h"
 #include "ImageContainer.h"
 #include "AudioCaptureStream.h"
 #include "AudioChannelService.h"
 #include "AudioNodeStream.h"
 #include "AudioNodeExternalInputStream.h"
+#include "MediaStreamListener.h"
 #include "mozilla/dom/AudioContextBinding.h"
 #include "mozilla/media/MediaUtils.h"
 #include <algorithm>
 #include "GeckoProfiler.h"
 #include "mozilla/unused.h"
 #include "mozilla/media/MediaUtils.h"
 #ifdef MOZ_WEBRTC
 #include "AudioOutputObserver.h"
@@ -55,16 +56,22 @@ LazyLogModule gMediaStreamGraphLog("Medi
 #    define LIFECYCLE_LOG(...)  __android_log_print(ANDROID_LOG_INFO, "Gecko - MSG", ## __VA_ARGS__); printf(__VA_ARGS__);printf("\n");
 #  else
 #    define LIFECYCLE_LOG(...) printf(__VA_ARGS__);printf("\n");
 #  endif
 #else
 #  define LIFECYCLE_LOG(...)
 #endif
 
+enum SourceMediaStream::TrackCommands : uint32_t {
+  TRACK_CREATE = TrackEventCommand::TRACK_EVENT_CREATED,
+  TRACK_END = TrackEventCommand::TRACK_EVENT_ENDED,
+  TRACK_UNUSED = TrackEventCommand::TRACK_EVENT_UNUSED,
+};
+
 /**
  * A hash table containing the graph instances, one per AudioChannel.
  */
 static nsDataHashtable<nsUint32HashKey, MediaStreamGraphImpl*> gGraphs;
 
 MediaStreamGraphImpl::~MediaStreamGraphImpl()
 {
   NS_ASSERTION(IsEmpty(),
@@ -196,17 +203,17 @@ MediaStreamGraphImpl::ExtractPendingInpu
         if (data->mCommands) {
           MOZ_ASSERT(!(data->mCommands & SourceMediaStream::TRACK_UNUSED));
           for (MediaStreamListener* l : aStream->mListeners) {
             if (data->mCommands & SourceMediaStream::TRACK_END) {
               l->NotifyQueuedAudioData(this, data->mID,
                                        offset, *(static_cast<AudioSegment*>(data->mData.get())));
             }
             l->NotifyQueuedTrackChanges(this, data->mID,
-                                        offset, data->mCommands, *data->mData);
+                                        offset, static_cast<TrackEventCommand>(data->mCommands), *data->mData);
             if (data->mCommands & SourceMediaStream::TRACK_CREATE) {
               l->NotifyQueuedAudioData(this, data->mID,
                                        offset, *(static_cast<AudioSegment*>(data->mData.get())));
             }
           }
         } else {
           for (MediaStreamListener* l : aStream->mListeners) {
               l->NotifyQueuedAudioData(this, data->mID,
@@ -216,27 +223,27 @@ MediaStreamGraphImpl::ExtractPendingInpu
       }
 
       // Video case.
       if (data->mData->GetType() == MediaSegment::VIDEO) {
         if (data->mCommands) {
           MOZ_ASSERT(!(data->mCommands & SourceMediaStream::TRACK_UNUSED));
           for (MediaStreamListener* l : aStream->mListeners) {
             l->NotifyQueuedTrackChanges(this, data->mID,
-                                        offset, data->mCommands, *data->mData);
+                                        offset, static_cast<TrackEventCommand>(data->mCommands), *data->mData);
           }
         } else {
           // Fixme: This part will be removed in the bug 1201363. It will be
           // removed in changeset "Do not copy video segment to StreamTracks in
           // TrackUnionStream."
 
           // Dealing with video and not TRACK_CREATE and TRACK_END case.
           for (MediaStreamListener* l : aStream->mListeners) {
             l->NotifyQueuedTrackChanges(this, data->mID,
-                                        offset, data->mCommands, *data->mData);
+                                        offset, static_cast<TrackEventCommand>(data->mCommands), *data->mData);
           }
         }
       }
 
       for (TrackBound<MediaStreamTrackListener>& b : aStream->mTrackListeners) {
         if (b.mTrackID != data->mID) {
           continue;
         }
@@ -351,17 +358,17 @@ MediaStreamGraphImpl::UpdateCurrentTimeF
     // out.
     if (stream->mFinished && !stream->mNotifiedFinished &&
         mProcessedTime >=
           stream->StreamTimeToGraphTime(stream->GetStreamTracks().GetAllTracksEnd())) {
       stream->mNotifiedFinished = true;
       SetStreamOrderDirty();
       for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
         MediaStreamListener* l = stream->mListeners[j];
-        l->NotifyEvent(this, MediaStreamListener::EVENT_FINISHED);
+        l->NotifyEvent(this, MediaStreamGraphEvent::EVENT_FINISHED);
       }
     }
   }
 }
 
 template<typename C, typename Chunk>
 void
 MediaStreamGraphImpl::ProcessChunkMetadataForInterval(MediaStream* aStream,
@@ -1996,16 +2003,24 @@ MediaStream::MediaStream()
   , mMainThreadDestroyed(false)
   , mNrOfMainThreadUsers(0)
   , mGraph(nullptr)
   , mAudioChannelType(dom::AudioChannel::Normal)
 {
   MOZ_COUNT_CTOR(MediaStream);
 }
 
+MediaStream::~MediaStream()
+{
+  MOZ_COUNT_DTOR(MediaStream);
+  NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
+  NS_ASSERTION(mMainThreadListeners.IsEmpty(),
+               "All main thread listeners should have been removed");
+}
+
 size_t
 MediaStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = 0;
 
   // Not owned:
   // - mGraph - Not reported here
   // - mConsumers - elements
@@ -2100,33 +2115,33 @@ StreamTracks::Track*
 MediaStream::EnsureTrack(TrackID aTrackId)
 {
   StreamTracks::Track* track = mTracks.FindTrack(aTrackId);
   if (!track) {
     nsAutoPtr<MediaSegment> segment(new AudioSegment());
     for (uint32_t j = 0; j < mListeners.Length(); ++j) {
       MediaStreamListener* l = mListeners[j];
       l->NotifyQueuedTrackChanges(Graph(), aTrackId, 0,
-                                  MediaStreamListener::TRACK_EVENT_CREATED,
+                                  TrackEventCommand::TRACK_EVENT_CREATED,
                                   *segment);
       // TODO If we ever need to ensure several tracks at once, we will have to
       // change this.
       l->NotifyFinishedTrackCreation(Graph());
     }
     track = &mTracks.AddTrack(aTrackId, 0, segment.forget());
   }
   return track;
 }
 
 void
 MediaStream::RemoveAllListenersImpl()
 {
   for (int32_t i = mListeners.Length() - 1; i >= 0; --i) {
     RefPtr<MediaStreamListener> listener = mListeners[i].forget();
-    listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
+    listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_REMOVED);
   }
   mListeners.Clear();
 }
 
 void
 MediaStream::DestroyImpl()
 {
   for (int32_t i = mConsumers.Length() - 1; i >= 0; --i) {
@@ -2376,27 +2391,27 @@ MediaStream::AddListenerImpl(already_Add
       // TrackUnionStream guarantees that each of its tracks has an input track.
       // Other types do not implement GetInputStreamFor() and will return null.
       inputStream = ps->GetInputStreamFor(it->GetID());
       MOZ_ASSERT(inputStream);
       inputTrackID = ps->GetInputTrackIDFor(it->GetID());
       MOZ_ASSERT(IsTrackIDExplicit(inputTrackID));
     }
 
-    uint32_t flags = MediaStreamListener::TRACK_EVENT_CREATED;
+    uint32_t flags = TrackEventCommand::TRACK_EVENT_CREATED;
     if (it->IsEnded()) {
-      flags |= MediaStreamListener::TRACK_EVENT_ENDED;
+      flags |= TrackEventCommand::TRACK_EVENT_ENDED;
     }
     nsAutoPtr<MediaSegment> segment(it->GetSegment()->CreateEmptyClone());
     listener->NotifyQueuedTrackChanges(Graph(), it->GetID(), it->GetEnd(),
-                                       flags, *segment,
+                                       static_cast<TrackEventCommand>(flags), *segment,
                                        inputStream, inputTrackID);
   }
   if (mNotifiedFinished) {
-    listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_FINISHED);
+    listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_FINISHED);
   }
   if (mNotifiedHasCurrentData) {
     listener->NotifyHasCurrentData(GraphImpl());
   }
 }
 
 void
 MediaStream::AddListener(MediaStreamListener* aListener)
@@ -2415,17 +2430,17 @@ MediaStream::AddListener(MediaStreamList
 }
 
 void
 MediaStream::RemoveListenerImpl(MediaStreamListener* aListener)
 {
   // wouldn't need this if we could do it in the opposite order
   RefPtr<MediaStreamListener> listener(aListener);
   mListeners.RemoveElement(aListener);
-  listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
+  listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_REMOVED);
 }
 
 void
 MediaStream::RemoveListener(MediaStreamListener* aListener)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, MediaStreamListener* aListener) :
@@ -2681,16 +2696,26 @@ MediaStream::AddMainThreadListener(MainT
 
     RefPtr<MediaStream> mStream;
   };
 
   nsCOMPtr<nsIRunnable> runnable = new NotifyRunnable(this);
   NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable.forget())));
 }
 
+SourceMediaStream::SourceMediaStream() :
+  MediaStream(),
+  mMutex("mozilla::media::SourceMediaStream"),
+  mUpdateKnownTracksTime(0),
+  mPullEnabled(false),
+  mUpdateFinished(false),
+  mNeedsMixing(false)
+{
+}
+
 nsresult
 SourceMediaStream::OpenAudioInput(int aID,
                                   AudioDataListener *aListener)
 {
   if (GraphImpl()) {
     mInputListener = aListener;
     return GraphImpl()->OpenAudioInput(aID, aListener);
   }
@@ -2746,16 +2771,23 @@ SourceMediaStream::AddTrackInternal(Trac
   data->mData = aSegment;
   ResampleAudioToGraphSampleRate(data, aSegment);
   if (!(aFlags & ADDTRACK_QUEUED) && GraphImpl()) {
     GraphImpl()->EnsureNextIteration();
   }
 }
 
 void
+SourceMediaStream::AddAudioTrack(TrackID aID, TrackRate aRate, StreamTime aStart,
+                                 AudioSegment* aSegment, uint32_t aFlags)
+{
+  AddTrackInternal(aID, aRate, aStart, aSegment, aFlags);
+}
+
+void
 SourceMediaStream::FinishAddTracks()
 {
   MutexAutoLock lock(mMutex);
   mUpdateTracks.AppendElements(Move(mPendingTracks));
   LIFECYCLE_LOG("FinishAddTracks: %lu/%lu", mPendingTracks.Length(), mUpdateTracks.Length());
   if (GraphImpl()) {
     GraphImpl()->EnsureNextIteration();
   }
@@ -2843,69 +2875,69 @@ SourceMediaStream::NotifyDirectConsumers
     }
     StreamTime offset = 0; // FIX! need a separate StreamTime.... or the end of the internal buffer
     source.mListener->NotifyRealtimeTrackDataAndApplyTrackDisabling(Graph(), offset, *aSegment);
   }
 }
 
 // These handle notifying all the listeners of an event
 void
-SourceMediaStream::NotifyListenersEventImpl(MediaStreamListener::MediaStreamGraphEvent aEvent)
+SourceMediaStream::NotifyListenersEventImpl(MediaStreamGraphEvent aEvent)
 {
   for (uint32_t j = 0; j < mListeners.Length(); ++j) {
     MediaStreamListener* l = mListeners[j];
     l->NotifyEvent(GraphImpl(), aEvent);
   }
 }
 
 void
-SourceMediaStream::NotifyListenersEvent(MediaStreamListener::MediaStreamGraphEvent aNewEvent)
+SourceMediaStream::NotifyListenersEvent(MediaStreamGraphEvent aNewEvent)
 {
   class Message : public ControlMessage {
   public:
-    Message(SourceMediaStream* aStream, MediaStreamListener::MediaStreamGraphEvent aEvent) :
+    Message(SourceMediaStream* aStream, MediaStreamGraphEvent aEvent) :
       ControlMessage(aStream), mEvent(aEvent) {}
     void Run() override
       {
         mStream->AsSourceStream()->NotifyListenersEventImpl(mEvent);
       }
-    MediaStreamListener::MediaStreamGraphEvent mEvent;
+    MediaStreamGraphEvent mEvent;
   };
   GraphImpl()->AppendMessage(MakeUnique<Message>(this, aNewEvent));
 }
 
 void
 SourceMediaStream::AddDirectListener(DirectMediaStreamListener* aListener)
 {
   bool wasEmpty;
   {
     MutexAutoLock lock(mMutex);
     wasEmpty = mDirectListeners.IsEmpty();
     mDirectListeners.AppendElement(aListener);
   }
 
   if (wasEmpty) {
     // Async
-    NotifyListenersEvent(MediaStreamListener::EVENT_HAS_DIRECT_LISTENERS);
+    NotifyListenersEvent(MediaStreamGraphEvent::EVENT_HAS_DIRECT_LISTENERS);
   }
 }
 
 void
 SourceMediaStream::RemoveDirectListener(DirectMediaStreamListener* aListener)
 {
   bool isEmpty;
   {
     MutexAutoLock lock(mMutex);
     mDirectListeners.RemoveElement(aListener);
     isEmpty = mDirectListeners.IsEmpty();
   }
 
   if (isEmpty) {
     // Async
-    NotifyListenersEvent(MediaStreamListener::EVENT_HAS_NO_DIRECT_LISTENERS);
+    NotifyListenersEvent(MediaStreamGraphEvent::EVENT_HAS_NO_DIRECT_LISTENERS);
   }
 }
 
 void
 SourceMediaStream::AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
                                               TrackID aTrackID)
 {
   MOZ_ASSERT(IsTrackIDExplicit(aTrackID));
@@ -2974,17 +3006,17 @@ SourceMediaStream::GetEndOfAppendedData(
 }
 
 void
 SourceMediaStream::EndTrack(TrackID aID)
 {
   MutexAutoLock lock(mMutex);
   TrackData *track = FindDataForTrack(aID);
   if (track) {
-    track->mCommands |= TRACK_END;
+    track->mCommands |= TrackEventCommand::TRACK_EVENT_ENDED;
   }
   if (auto graph = GraphImpl()) {
     graph->EnsureNextIteration();
   }
 }
 
 void
 SourceMediaStream::AdvanceKnownTracksTime(StreamTime aKnownTime)
@@ -3033,23 +3065,27 @@ SourceMediaStream::SetTrackEnabledImpl(T
 }
 
 void
 SourceMediaStream::EndAllTrackAndFinish()
 {
   MutexAutoLock lock(mMutex);
   for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
     SourceMediaStream::TrackData* data = &mUpdateTracks[i];
-    data->mCommands |= TRACK_END;
+    data->mCommands |= TrackEventCommand::TRACK_EVENT_ENDED;
   }
   mPendingTracks.Clear();
   FinishWithLockHeld();
   // we will call NotifyEvent() to let GetUserMedia know
 }
 
+SourceMediaStream::~SourceMediaStream()
+{
+}
+
 void
 SourceMediaStream::RegisterForAudioMixing()
 {
   MutexAutoLock lock(mMutex);
   mNeedsMixing = true;
 }
 
 bool
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -7,24 +7,23 @@
 #define MOZILLA_MEDIASTREAMGRAPH_H_
 
 #include "mozilla/LinkedList.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/TaskQueue.h"
 
 #include "mozilla/dom/AudioChannelBinding.h"
 
-#include "AudioSegment.h"
 #include "AudioStream.h"
 #include "nsTArray.h"
 #include "nsIRunnable.h"
-#include "StreamTracks.h"
 #include "VideoFrameContainer.h"
 #include "VideoSegment.h"
 #include "MainThreadUtils.h"
+#include "StreamTracks.h"
 #include "nsAutoPtr.h"
 #include "nsAutoRef.h"
 #include <speex/speex_resampler.h>
 
 class nsIRunnable;
 
 template <>
 class nsAutoRefTraits<SpeexResamplerState> : public nsPointerRefTraits<SpeexResamplerState>
@@ -84,135 +83,16 @@ class AudioNodeStream;
 class CameraPreviewMediaStream;
 class MediaInputPort;
 class MediaStream;
 class MediaStreamGraph;
 class MediaStreamGraphImpl;
 class ProcessedMediaStream;
 class SourceMediaStream;
 
-/**
- * This is a base class for media graph thread listener callbacks.
- * Override methods to be notified of audio or video data or changes in stream
- * state.
- *
- * This can be used by stream recorders or network connections that receive
- * stream input. It could also be used for debugging.
- *
- * All notification methods are called from the media graph thread. Overriders
- * of these methods are responsible for all synchronization. Beware!
- * These methods are called without the media graph monitor held, so
- * reentry into media graph methods is possible, although very much discouraged!
- * You should do something non-blocking and non-reentrant (e.g. dispatch an
- * event to some thread) and return.
- * The listener is not allowed to add/remove any listeners from the stream.
- *
- * When a listener is first attached, we guarantee to send a NotifyBlockingChanged
- * callback to notify of the initial blocking state. Also, if a listener is
- * attached to a stream that has already finished, we'll call NotifyFinished.
- */
-class MediaStreamListener {
-protected:
-  // Protected destructor, to discourage deletion outside of Release():
-  virtual ~MediaStreamListener() {}
-
-public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener)
-
-  /**
-   * When a SourceMediaStream has pulling enabled, and the MediaStreamGraph
-   * control loop is ready to pull, this gets called. A NotifyPull implementation
-   * is allowed to call the SourceMediaStream methods that alter track
-   * data. It is not allowed to make other MediaStream API calls, including
-   * calls to add or remove MediaStreamListeners. It is not allowed to block
-   * for any length of time.
-   * aDesiredTime is the stream time we would like to get data up to. Data
-   * beyond this point will not be played until NotifyPull runs again, so there's
-   * not much point in providing it. Note that if the stream is blocked for
-   * some reason, then data before aDesiredTime may not be played immediately.
-   */
-  virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) {}
-
-  enum Blocking {
-    BLOCKED,
-    UNBLOCKED
-  };
-  /**
-   * Notify that the blocking status of the stream changed. The initial state
-   * is assumed to be BLOCKED.
-   */
-  virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) {}
-
-  /**
-   * Notify that the stream has data in each track
-   * for the stream's current time. Once this state becomes true, it will
-   * always be true since we block stream time from progressing to times where
-   * there isn't data in each track.
-   */
-  virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) {}
-
-  /**
-   * Notify that the stream output is advancing. aCurrentTime is the graph's
-   * current time. MediaStream::GraphTimeToStreamTime can be used to get the
-   * stream time.
-   */
-  virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) {}
-
-  enum MediaStreamGraphEvent {
-    EVENT_FINISHED,
-    EVENT_REMOVED,
-    EVENT_HAS_DIRECT_LISTENERS, // transition from no direct listeners
-    EVENT_HAS_NO_DIRECT_LISTENERS,  // transition to no direct listeners
-  };
-
-  /**
-   * Notify that an event has occurred on the Stream
-   */
-  virtual void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent aEvent) {}
-
-  // maskable flags, not a simple enumerated value
-  enum {
-    TRACK_EVENT_CREATED = 0x01,
-    TRACK_EVENT_ENDED = 0x02,
-    TRACK_EVENT_UNUSED = ~(TRACK_EVENT_ENDED | TRACK_EVENT_CREATED),
-  };
-  /**
-   * Notify that changes to one of the stream tracks have been queued.
-   * aTrackEvents can be any combination of TRACK_EVENT_CREATED and
-   * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track
-   * at aTrackOffset (relative to the start of the stream).
-   * aInputStream and aInputTrackID will be set if the changes originated
-   * from an input stream's track. In practice they will only be used for
-   * ProcessedMediaStreams.
-   */
-  virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
-                                        StreamTime aTrackOffset,
-                                        uint32_t aTrackEvents,
-                                        const MediaSegment& aQueuedMedia,
-                                        MediaStream* aInputStream = nullptr,
-                                        TrackID aInputTrackID = TRACK_INVALID) {}
-
-  /**
-   * Notify queued audio data. Only audio data need to be queued. The video data
-   * will be notified by MediaStreamVideoSink::SetCurrentFrame.
-   */
-  virtual void NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
-                                     StreamTime aTrackOffset,
-                                     const AudioSegment& aQueuedMedia,
-                                     MediaStream* aInputStream = nullptr,
-                                     TrackID aInputTrackID = TRACK_INVALID) {}
-
-  /**
-   * Notify that all new tracks this iteration have been created.
-   * This is to ensure that tracks added atomically to MediaStreamGraph
-   * are also notified of atomically to MediaStreamListeners.
-   */
-  virtual void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) {}
-};
-
 class AudioDataListenerInterface {
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~AudioDataListenerInterface() {}
 
 public:
   /* These are for cubeb audio input & output streams: */
   /**
@@ -242,187 +122,16 @@ protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~AudioDataListener() {}
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioDataListener)
 };
 
 /**
- * This is a base class for media graph thread listener callbacks locked to
- * specific tracks. Override methods to be notified of audio or video data or
- * changes in track state.
- *
- * All notification methods are called from the media graph thread. Overriders
- * of these methods are responsible for all synchronization. Beware!
- * These methods are called without the media graph monitor held, so
- * reentry into media graph methods is possible, although very much discouraged!
- * You should do something non-blocking and non-reentrant (e.g. dispatch an
- * event to some thread) and return.
- * The listener is not allowed to add/remove any listeners from the parent
- * stream.
- *
- * If a listener is attached to a track that has already ended, we guarantee
- * to call NotifyEnded.
- */
-class MediaStreamTrackListener
-{
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamTrackListener)
-
-public:
-  virtual void NotifyQueuedChanges(MediaStreamGraph* aGraph,
-                                   StreamTime aTrackOffset,
-                                   const MediaSegment& aQueuedMedia) {}
-
-  virtual void NotifyPrincipalHandleChanged(MediaStreamGraph* aGraph,
-                                            const PrincipalHandle& aNewPrincipalHandle) {}
-
-  virtual void NotifyEnded() {}
-
-  virtual void NotifyRemoved() {}
-
-protected:
-  virtual ~MediaStreamTrackListener() {}
-};
-
-
-/**
- * This is a base class for media graph thread listener direct callbacks
- * from within AppendToTrack(). Note that your regular listener will
- * still get NotifyQueuedTrackChanges() callbacks from the MSG thread, so
- * you must be careful to ignore them if AddDirectListener was successful.
- */
-class DirectMediaStreamListener : public MediaStreamListener
-{
-public:
-  virtual ~DirectMediaStreamListener() {}
-
-  /*
-   * This will be called on any DirectMediaStreamListener added to a
-   * a SourceMediaStream when AppendToTrack() is called.  The MediaSegment
-   * will be the RawSegment (unresampled) if available in AppendToTrack().
-   * Note that NotifyQueuedTrackChanges() calls will also still occur.
-   */
-  virtual void NotifyRealtimeData(MediaStreamGraph* aGraph, TrackID aID,
-                                  StreamTime aTrackOffset,
-                                  uint32_t aTrackEvents,
-                                  const MediaSegment& aMedia) {}
-};
-
-/**
- * This is a base class for media graph thread listener direct callbacks from
- * within AppendToTrack(). It is bound to a certain track and can only be
- * installed on audio tracks. Once added to a track on any stream in the graph,
- * the graph will try to install it at that track's source of media data.
- *
- * This works for TrackUnionStreams, which will forward the listener to the
- * track's input track if it exists, or wait for it to be created before
- * forwarding if it doesn't.
- * Once it reaches a SourceMediaStream, it can be successfully installed.
- * Other types of streams will fail installation since they are not supported.
- *
- * Note that this listener and others for the same track will still get
- * NotifyQueuedChanges() callbacks from the MSG tread, so you must be careful
- * to ignore them if this listener was successfully installed.
- */
-class DirectMediaStreamTrackListener : public MediaStreamTrackListener
-{
-  friend class SourceMediaStream;
-  friend class TrackUnionStream;
-
-public:
-  /*
-   * This will be called on any DirectMediaStreamTrackListener added to a
-   * SourceMediaStream when AppendToTrack() is called for the listener's bound
-   * track, using the thread of the AppendToTrack() caller. The MediaSegment
-   * will be the RawSegment (unresampled) if available in AppendToTrack().
-   * If the track is enabled at the source but has been disabled in one of the
-   * streams in between the source and where it was originally added, aMedia
-   * will be a disabled version of the one passed to AppendToTrack() as well.
-   * Note that NotifyQueuedTrackChanges() calls will also still occur.
-   */
-  virtual void NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
-                                       StreamTime aTrackOffset,
-                                       const MediaSegment& aMedia) {}
-
-  /**
-   * When a direct listener is processed for installation by the
-   * MediaStreamGraph it will be notified with whether the installation was
-   * successful or not. The results of this installation are the following:
-   * TRACK_NOT_FOUND_AT_SOURCE
-   *    We found the source stream of media data for this track, but the track
-   *    didn't exist. This should only happen if you try to install the listener
-   *    directly to a SourceMediaStream that doesn't contain the given TrackID.
-   * TRACK_TYPE_NOT_SUPPORTED
-   *    This is the failure when you install the listener to a non-audio track.
-   * STREAM_NOT_SUPPORTED
-   *    While looking for the data source of this track, we found a MediaStream
-   *    that is not a SourceMediaStream or a TrackUnionStream.
-   * SUCCESS
-   *    Installation was successful and this listener will start receiving
-   *    NotifyRealtimeData on the next AppendToTrack().
-   */
-  enum class InstallationResult {
-    TRACK_NOT_FOUND_AT_SOURCE,
-    TRACK_TYPE_NOT_SUPPORTED,
-    STREAM_NOT_SUPPORTED,
-    SUCCESS
-  };
-  virtual void NotifyDirectListenerInstalled(InstallationResult aResult) {}
-  virtual void NotifyDirectListenerUninstalled() {}
-
-protected:
-  virtual ~DirectMediaStreamTrackListener() {}
-
-  void MirrorAndDisableSegment(AudioSegment& aFrom, AudioSegment& aTo)
-  {
-    aTo.Clear();
-    aTo.AppendNullData(aFrom.GetDuration());
-  }
-
-  void NotifyRealtimeTrackDataAndApplyTrackDisabling(MediaStreamGraph* aGraph,
-                                                     StreamTime aTrackOffset,
-                                                     MediaSegment& aMedia)
-  {
-    if (mDisabledCount == 0) {
-      NotifyRealtimeTrackData(aGraph, aTrackOffset, aMedia);
-      return;
-    }
-
-    if (!mMedia) {
-      mMedia = aMedia.CreateEmptyClone();
-    }
-    if (aMedia.GetType() == MediaSegment::AUDIO) {
-      MirrorAndDisableSegment(static_cast<AudioSegment&>(aMedia),
-                              static_cast<AudioSegment&>(*mMedia));
-    } else {
-      MOZ_CRASH("Unsupported media type");
-    }
-    NotifyRealtimeTrackData(aGraph, aTrackOffset, *mMedia);
-  }
-
-  void IncreaseDisabled()
-  {
-    ++mDisabledCount;
-  }
-  void DecreaseDisabled()
-  {
-    --mDisabledCount;
-    MOZ_ASSERT(mDisabledCount >= 0, "Double decrease");
-  }
-
-  // Matches the number of disabled streams to which this listener is attached.
-  // The number of streams are those between the stream the listener was added
-  // and the SourceMediaStream that is the input of the data.
-  Atomic<int32_t> mDisabledCount;
-
-  nsAutoPtr<MediaSegment> mMedia;
-};
-
-/**
  * This is a base class for main-thread listener callbacks.
  * This callback is invoked on the main thread when the main-thread-visible
  * state of a stream has changed.
  *
  * These methods are called with the media graph monitor held, so
  * reentry into general media graph methods is not possible.
  * You should do something non-blocking and non-reentrant (e.g. dispatch an
  * event) and return. DispatchFromMainThreadAfterNextStreamStateUpdate
@@ -442,16 +151,33 @@ struct AudioNodeSizes
 {
   AudioNodeSizes() : mDomNode(0), mStream(0), mEngine(0), mNodeType() {}
   size_t mDomNode;
   size_t mStream;
   size_t mEngine;
   nsCString mNodeType;
 };
 
+class AudioNodeEngine;
+class AudioNodeExternalInputStream;
+class AudioNodeStream;
+class AudioSegment;
+class CameraPreviewMediaStream;
+class DirectMediaStreamListener;
+class DirectMediaStreamTrackListener;
+class MediaInputPort;
+class MediaStreamGraphImpl;
+class MediaStreamListener;
+class MediaStreamTrackListener;
+class ProcessedMediaStream;
+class SourceMediaStream;
+
+enum MediaStreamGraphEvent : uint32_t;
+enum TrackEventCommand : uint32_t;
+
 /**
  * Helper struct for binding a track listener to a specific TrackID.
  */
 template<typename Listener>
 struct TrackBound
 {
   RefPtr<Listener> mListener;
   TrackID mTrackID;
@@ -531,23 +257,17 @@ class MediaStream : public mozilla::Link
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStream)
 
   MediaStream();
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
-  virtual ~MediaStream()
-  {
-    MOZ_COUNT_DTOR(MediaStream);
-    NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
-    NS_ASSERTION(mMainThreadListeners.IsEmpty(),
-                 "All main thread listeners should have been removed");
-  }
+  virtual ~MediaStream();
 
 public:
   /**
    * Returns the graph that owns this stream.
    */
   MediaStreamGraphImpl* GraphImpl();
   MediaStreamGraph* Graph();
   /**
@@ -859,17 +579,17 @@ protected:
 
   // Client-set volume of this stream
   struct AudioOutput {
     explicit AudioOutput(void* aKey) : mKey(aKey), mVolume(1.0f) {}
     void* mKey;
     float mVolume;
   };
   nsTArray<AudioOutput> mAudioOutputs;
-  nsTArray<RefPtr<VideoFrameContainer> > mVideoOutputs;
+  nsTArray<RefPtr<VideoFrameContainer>> mVideoOutputs;
   // We record the last played video frame to avoid playing the frame again
   // with a different frame id.
   VideoFrame mLastPlayedVideoFrame;
   nsTArray<RefPtr<MediaStreamListener> > mListeners;
   nsTArray<TrackBound<MediaStreamTrackListener>> mTrackListeners;
   nsTArray<MainThreadMediaStreamListener*> mMainThreadListeners;
   nsTArray<TrackID> mDisabledTrackIDs;
 
@@ -948,24 +668,17 @@ protected:
  * This is a stream into which a decoder can write audio and video.
  *
  * Audio and video can be written on any thread, but you probably want to
  * always write from the same thread to avoid unexpected interleavings.
  */
 class SourceMediaStream : public MediaStream
 {
 public:
-  explicit SourceMediaStream() :
-    MediaStream(),
-    mMutex("mozilla::media::SourceMediaStream"),
-    mUpdateKnownTracksTime(0),
-    mPullEnabled(false),
-    mUpdateFinished(false),
-    mNeedsMixing(false)
-  {}
+  explicit SourceMediaStream();
 
   SourceMediaStream* AsSourceStream() override { return this; }
 
   // Media graph thread only
 
   // Users of audio inputs go through the stream so it can track when the
   // last stream referencing an input goes away, so it can close the cubeb
   // input.  Also note: callable on any thread (though it bounces through
@@ -987,18 +700,18 @@ public:
    */
   void SetPullEnabled(bool aEnabled);
 
   /**
    * These add/remove DirectListeners, which allow bypassing the graph and any
    * synchronization delays for e.g. PeerConnection, which wants the data ASAP
    * and lets the far-end handle sync and playout timing.
    */
-  void NotifyListenersEventImpl(MediaStreamListener::MediaStreamGraphEvent aEvent);
-  void NotifyListenersEvent(MediaStreamListener::MediaStreamGraphEvent aEvent);
+  void NotifyListenersEventImpl(MediaStreamGraphEvent aEvent);
+  void NotifyListenersEvent(MediaStreamGraphEvent aEvent);
   void AddDirectListener(DirectMediaStreamListener* aListener);
   void RemoveDirectListener(DirectMediaStreamListener* aListener);
 
   enum {
     ADDTRACK_QUEUED    = 0x01 // Queue track add until FinishAddTracks()
   };
   /**
    * Add a new track to the stream starting at the given base time (which
@@ -1011,20 +724,17 @@ public:
   {
     AddTrackInternal(aID, GraphRate(), aStart, aSegment, aFlags);
   }
 
   /**
    * Like AddTrack, but resamples audio from aRate to the graph rate.
    */
   void AddAudioTrack(TrackID aID, TrackRate aRate, StreamTime aStart,
-                     AudioSegment* aSegment, uint32_t aFlags = 0)
-  {
-    AddTrackInternal(aID, aRate, aStart, aSegment, aFlags);
-  }
+                     AudioSegment* aSegment, uint32_t aFlags = 0);
 
   /**
    * Call after a series of AddTrack or AddAudioTrack calls to implement
    * any pending track adds.
    */
   void FinishAddTracks();
 
   /**
@@ -1090,21 +800,20 @@ public:
    */
   bool HasPendingAudioTrack();
 
   // XXX need a Reset API
 
   friend class MediaStreamGraphImpl;
 
 protected:
-  enum TrackCommands {
-    TRACK_CREATE = MediaStreamListener::TRACK_EVENT_CREATED,
-    TRACK_END = MediaStreamListener::TRACK_EVENT_ENDED,
-    TRACK_UNUSED = MediaStreamListener::TRACK_EVENT_UNUSED,
-  };
+  enum TrackCommands : uint32_t;
+
+  virtual ~SourceMediaStream();
+
   /**
    * Data for each track that hasn't ended.
    */
   struct TrackData {
     TrackID mID;
     // Sample rate of the input data.
     TrackRate mInputRate;
     // Resampler if the rate of the input track does not match the
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaStreamListener.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* 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/. */
+
+#include "MediaStreamListener.h"
+
+#include "AudioSegment.h"
+#include "VideoSegment.h"
+#include "StreamTracks.h"
+
+namespace mozilla {
+
+void
+DirectMediaStreamTrackListener::MirrorAndDisableSegment(AudioSegment& aFrom,
+                                                       AudioSegment& aTo)
+{
+  aTo.Clear();
+  aTo.AppendNullData(aFrom.GetDuration());
+}
+
+void
+DirectMediaStreamTrackListener::MirrorAndDisableSegment(VideoSegment& aFrom,
+                                                       VideoSegment& aTo)
+{
+  aTo.Clear();
+  for (VideoSegment::ChunkIterator it(aFrom); !it.IsEnded(); it.Next()) {
+    aTo.AppendFrame(do_AddRef(it->mFrame.GetImage()), it->GetDuration(),
+                    it->mFrame.GetIntrinsicSize(), it->GetPrincipalHandle(), true);
+  }
+}
+
+void
+DirectMediaStreamTrackListener::NotifyRealtimeTrackDataAndApplyTrackDisabling(MediaStreamGraph* aGraph,
+                                                                             StreamTime aTrackOffset,
+                                                                             MediaSegment& aMedia)
+{
+  if (mDisabledCount == 0) {
+    NotifyRealtimeTrackData(aGraph, aTrackOffset, aMedia);
+    return;
+  }
+
+  if (!mMedia) {
+    mMedia = aMedia.CreateEmptyClone();
+  }
+  if (aMedia.GetType() == MediaSegment::AUDIO) {
+    MirrorAndDisableSegment(static_cast<AudioSegment&>(aMedia),
+                            static_cast<AudioSegment&>(*mMedia));
+  } else if (aMedia.GetType() == MediaSegment::VIDEO) {
+    MirrorAndDisableSegment(static_cast<VideoSegment&>(aMedia),
+                            static_cast<VideoSegment&>(*mMedia));
+  } else {
+    MOZ_CRASH("Unsupported media type");
+  }
+  NotifyRealtimeTrackData(aGraph, aTrackOffset, *mMedia);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaStreamListener.h
@@ -0,0 +1,289 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_MEDIASTREAMLISTENER_h_
+#define MOZILLA_MEDIASTREAMLISTENER_h_
+
+namespace mozilla {
+
+class MediaStream;
+class MediaStreamGraph;
+
+enum MediaStreamGraphEvent : uint32_t {
+  EVENT_FINISHED,
+  EVENT_REMOVED,
+  EVENT_HAS_DIRECT_LISTENERS, // transition from no direct listeners
+  EVENT_HAS_NO_DIRECT_LISTENERS,  // transition to no direct listeners
+};
+
+// maskable flags, not a simple enumerated value
+enum TrackEventCommand : uint32_t {
+  TRACK_EVENT_NONE = 0x00,
+  TRACK_EVENT_CREATED = 0x01,
+  TRACK_EVENT_ENDED = 0x02,
+  TRACK_EVENT_UNUSED = ~(TRACK_EVENT_ENDED | TRACK_EVENT_CREATED),
+};
+
+/**
+ * This is a base class for media graph thread listener callbacks.
+ * Override methods to be notified of audio or video data or changes in stream
+ * state.
+ *
+ * This can be used by stream recorders or network connections that receive
+ * stream input. It could also be used for debugging.
+ *
+ * All notification methods are called from the media graph thread. Overriders
+ * of these methods are responsible for all synchronization. Beware!
+ * These methods are called without the media graph monitor held, so
+ * reentry into media graph methods is possible, although very much discouraged!
+ * You should do something non-blocking and non-reentrant (e.g. dispatch an
+ * event to some thread) and return.
+ * The listener is not allowed to add/remove any listeners from the stream.
+ *
+ * When a listener is first attached, we guarantee to send a NotifyBlockingChanged
+ * callback to notify of the initial blocking state. Also, if a listener is
+ * attached to a stream that has already finished, we'll call NotifyFinished.
+ */
+class MediaStreamListener {
+protected:
+  // Protected destructor, to discourage deletion outside of Release():
+  virtual ~MediaStreamListener() {}
+
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener)
+
+  /**
+   * When a SourceMediaStream has pulling enabled, and the MediaStreamGraph
+   * control loop is ready to pull, this gets called. A NotifyPull implementation
+   * is allowed to call the SourceMediaStream methods that alter track
+   * data. It is not allowed to make other MediaStream API calls, including
+   * calls to add or remove MediaStreamListeners. It is not allowed to block
+   * for any length of time.
+   * aDesiredTime is the stream time we would like to get data up to. Data
+   * beyond this point will not be played until NotifyPull runs again, so there's
+   * not much point in providing it. Note that if the stream is blocked for
+   * some reason, then data before aDesiredTime may not be played immediately.
+   */
+  virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) {}
+
+  enum Blocking {
+    BLOCKED,
+    UNBLOCKED
+  };
+  /**
+   * Notify that the blocking status of the stream changed. The initial state
+   * is assumed to be BLOCKED.
+   */
+  virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) {}
+
+  /**
+   * Notify that the stream has data in each track
+   * for the stream's current time. Once this state becomes true, it will
+   * always be true since we block stream time from progressing to times where
+   * there isn't data in each track.
+   */
+  virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) {}
+
+  /**
+   * Notify that the stream output is advancing. aCurrentTime is the graph's
+   * current time. MediaStream::GraphTimeToStreamTime can be used to get the
+   * stream time.
+   */
+  virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) {}
+
+  /**
+   * Notify that an event has occurred on the Stream
+   */
+  virtual void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent aEvent) {}
+
+  /**
+   * Notify that changes to one of the stream tracks have been queued.
+   * aTrackEvents can be any combination of TRACK_EVENT_CREATED and
+   * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track
+   * at aTrackOffset (relative to the start of the stream).
+   * aInputStream and aInputTrackID will be set if the changes originated
+   * from an input stream's track. In practice they will only be used for
+   * ProcessedMediaStreams.
+   */
+  virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
+                                        StreamTime aTrackOffset,
+                                        TrackEventCommand aTrackEvents,
+                                        const MediaSegment& aQueuedMedia,
+                                        MediaStream* aInputStream = nullptr,
+                                        TrackID aInputTrackID = TRACK_INVALID) {}
+
+  /**
+   * Notify queued audio data. Only audio data need to be queued. The video data
+   * will be notified by MediaStreamVideoSink::SetCurrentFrame.
+   */
+  virtual void NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
+                                     StreamTime aTrackOffset,
+                                     const AudioSegment& aQueuedMedia,
+                                     MediaStream* aInputStream = nullptr,
+                                     TrackID aInputTrackID = TRACK_INVALID) {}
+
+  /**
+   * Notify that all new tracks this iteration have been created.
+   * This is to ensure that tracks added atomically to MediaStreamGraph
+   * are also notified of atomically to MediaStreamListeners.
+   */
+  virtual void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) {}
+};
+
+/**
+ * This is a base class for media graph thread listener callbacks locked to
+ * specific tracks. Override methods to be notified of audio or video data or
+ * changes in track state.
+ *
+ * All notification methods are called from the media graph thread. Overriders
+ * of these methods are responsible for all synchronization. Beware!
+ * These methods are called without the media graph monitor held, so
+ * reentry into media graph methods is possible, although very much discouraged!
+ * You should do something non-blocking and non-reentrant (e.g. dispatch an
+ * event to some thread) and return.
+ * The listener is not allowed to add/remove any listeners from the parent
+ * stream.
+ *
+ * If a listener is attached to a track that has already ended, we guarantee
+ * to call NotifyEnded.
+ */
+class MediaStreamTrackListener
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamTrackListener)
+
+public:
+  virtual void NotifyQueuedChanges(MediaStreamGraph* aGraph,
+                                   StreamTime aTrackOffset,
+                                   const MediaSegment& aQueuedMedia) {}
+
+  virtual void NotifyPrincipalHandleChanged(MediaStreamGraph* aGraph,
+                                            const PrincipalHandle& aNewPrincipalHandle) {}
+
+  virtual void NotifyEnded() {}
+
+  virtual void NotifyRemoved() {}
+
+protected:
+  virtual ~MediaStreamTrackListener() {}
+};
+
+
+/**
+ * This is a base class for media graph thread listener direct callbacks
+ * from within AppendToTrack(). Note that your regular listener will
+ * still get NotifyQueuedTrackChanges() callbacks from the MSG thread, so
+ * you must be careful to ignore them if AddDirectListener was successful.
+ */
+class DirectMediaStreamListener : public MediaStreamListener
+{
+public:
+  virtual ~DirectMediaStreamListener() {}
+
+  /*
+   * This will be called on any DirectMediaStreamListener added to a
+   * a SourceMediaStream when AppendToTrack() is called.  The MediaSegment
+   * will be the RawSegment (unresampled) if available in AppendToTrack().
+   * Note that NotifyQueuedTrackChanges() calls will also still occur.
+   */
+  virtual void NotifyRealtimeData(MediaStreamGraph* aGraph, TrackID aID,
+                                  StreamTime aTrackOffset,
+                                  uint32_t aTrackEvents,
+                                  const MediaSegment& aMedia) {}
+};
+
+/**
+ * This is a base class for media graph thread listener direct callbacks from
+ * within AppendToTrack(). It is bound to a certain track and can only be
+ * installed on audio tracks. Once added to a track on any stream in the graph,
+ * the graph will try to install it at that track's source of media data.
+ *
+ * This works for TrackUnionStreams, which will forward the listener to the
+ * track's input track if it exists, or wait for it to be created before
+ * forwarding if it doesn't.
+ * Once it reaches a SourceMediaStream, it can be successfully installed.
+ * Other types of streams will fail installation since they are not supported.
+ *
+ * Note that this listener and others for the same track will still get
+ * NotifyQueuedChanges() callbacks from the MSG tread, so you must be careful
+ * to ignore them if this listener was successfully installed.
+ */
+class DirectMediaStreamTrackListener : public MediaStreamTrackListener
+{
+  friend class SourceMediaStream;
+  friend class TrackUnionStream;
+
+public:
+  /*
+   * This will be called on any DirectMediaStreamTrackListener added to a
+   * SourceMediaStream when AppendToTrack() is called for the listener's bound
+   * track, using the thread of the AppendToTrack() caller. The MediaSegment
+   * will be the RawSegment (unresampled) if available in AppendToTrack().
+   * If the track is enabled at the source but has been disabled in one of the
+   * streams in between the source and where it was originally added, aMedia
+   * will be a disabled version of the one passed to AppendToTrack() as well.
+   * Note that NotifyQueuedTrackChanges() calls will also still occur.
+   */
+  virtual void NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
+                                       StreamTime aTrackOffset,
+                                       const MediaSegment& aMedia) {}
+
+  /**
+   * When a direct listener is processed for installation by the
+   * MediaStreamGraph it will be notified with whether the installation was
+   * successful or not. The results of this installation are the following:
+   * TRACK_NOT_FOUND_AT_SOURCE
+   *    We found the source stream of media data for this track, but the track
+   *    didn't exist. This should only happen if you try to install the listener
+   *    directly to a SourceMediaStream that doesn't contain the given TrackID.
+   * TRACK_TYPE_NOT_SUPPORTED
+   *    This is the failure when you install the listener to a
+   *    non-(audio or video) track.
+   * STREAM_NOT_SUPPORTED
+   *    While looking for the data source of this track, we found a MediaStream
+   *    that is not a SourceMediaStream or a TrackUnionStream.
+   * SUCCESS
+   *    Installation was successful and this listener will start receiving
+   *    NotifyRealtimeData on the next AppendToTrack().
+   */
+  enum class InstallationResult {
+    TRACK_NOT_FOUND_AT_SOURCE,
+    TRACK_TYPE_NOT_SUPPORTED,
+    STREAM_NOT_SUPPORTED,
+    SUCCESS
+  };
+  virtual void NotifyDirectListenerInstalled(InstallationResult aResult) {}
+  virtual void NotifyDirectListenerUninstalled() {}
+
+protected:
+  virtual ~DirectMediaStreamTrackListener() {}
+
+  void MirrorAndDisableSegment(AudioSegment& aFrom, AudioSegment& aTo);
+  void MirrorAndDisableSegment(VideoSegment& aFrom, VideoSegment& aTo);
+  void NotifyRealtimeTrackDataAndApplyTrackDisabling(MediaStreamGraph* aGraph,
+                                                     StreamTime aTrackOffset,
+                                                     MediaSegment& aMedia);
+
+  void IncreaseDisabled()
+  {
+    ++mDisabledCount;
+  }
+  void DecreaseDisabled()
+  {
+    --mDisabledCount;
+    MOZ_ASSERT(mDisabledCount >= 0, "Double decrease");
+  }
+
+  // Matches the number of disabled streams to which this listener is attached.
+  // The number of streams are those between the stream the listener was added
+  // and the SourceMediaStream that is the input of the data.
+  Atomic<int32_t> mDisabledCount;
+
+  nsAutoPtr<MediaSegment> mMedia;
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_MEDIASTREAMLISTENER_h_
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* 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/. */
 
 #include "MediaStreamGraphImpl.h"
+#include "MediaStreamListener.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/unused.h"
 
 #include "AudioSegment.h"
 #include "VideoSegment.h"
 #include "nsContentUtils.h"
 #include "nsIAppShell.h"
 #include "nsIObserver.h"
@@ -195,17 +196,17 @@ TrackUnionStream::TrackUnionStream() :
     // samples in our input stream to go just beyond the destination time.
     StreamTime outputStart = GraphTimeToStreamTimeWithBlocking(aFrom);
 
     nsAutoPtr<MediaSegment> segment;
     segment = aTrack->GetSegment()->CreateEmptyClone();
     for (uint32_t j = 0; j < mListeners.Length(); ++j) {
       MediaStreamListener* l = mListeners[j];
       l->NotifyQueuedTrackChanges(Graph(), id, outputStart,
-                                  MediaStreamListener::TRACK_EVENT_CREATED,
+                                  TrackEventCommand::TRACK_EVENT_CREATED,
                                   *segment,
                                   aPort->GetSource(), aTrack->GetID());
     }
     segment->AppendNullData(outputStart);
     StreamTracks::Track* track =
       &mTracks.AddTrack(id, outputStart, segment.forget());
     STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p added track %d for input stream %p track %d, start ticks %lld",
                                  this, track->GetID(), aPort->GetSource(), aTrack->GetID(),
@@ -251,17 +252,17 @@ TrackUnionStream::TrackUnionStream() :
       return;
     STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p ending track %d", this, outputTrack->GetID()));
     for (uint32_t j = 0; j < mListeners.Length(); ++j) {
       MediaStreamListener* l = mListeners[j];
       StreamTime offset = outputTrack->GetSegment()->GetDuration();
       nsAutoPtr<MediaSegment> segment;
       segment = outputTrack->GetSegment()->CreateEmptyClone();
       l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(), offset,
-                                  MediaStreamListener::TRACK_EVENT_ENDED,
+                                  TrackEventCommand::TRACK_EVENT_ENDED,
                                   *segment,
                                   mTrackMap[aIndex].mInputPort->GetSource(),
                                   mTrackMap[aIndex].mInputTrackID);
     }
     for (TrackBound<MediaStreamTrackListener>& b : mTrackListeners) {
       if (b.mTrackID == outputTrack->GetID()) {
         b.mListener->NotifyEnded();
       }
@@ -329,17 +330,17 @@ TrackUnionStream::TrackUnionStream() :
           l->NotifyQueuedAudioData(Graph(), outputTrack->GetID(),
                                    outputStart,
                                    *static_cast<AudioSegment*>(segment),
                                    map->mInputPort->GetSource(),
                                    map->mInputTrackID);
         } else {
           // This part will be removed in bug 1201363.
           l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(),
-                                      outputStart, 0, *segment,
+                                      outputStart, TrackEventCommand::TRACK_EVENT_NONE, *segment,
                                       map->mInputPort->GetSource(),
                                       map->mInputTrackID);
         }
       }
       for (TrackBound<MediaStreamTrackListener>& b : mTrackListeners) {
         if (b.mTrackID != outputTrack->GetID()) {
           continue;
         }
--- a/dom/media/encoder/MediaEncoder.cpp
+++ b/dom/media/encoder/MediaEncoder.cpp
@@ -61,25 +61,25 @@ MediaEncoder::NotifyRealtimeData(MediaSt
     }
   }
 }
 
 void
 MediaEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
                                        TrackID aID,
                                        StreamTime aTrackOffset,
-                                       uint32_t aTrackEvents,
+                                       TrackEventCommand aTrackEvents,
                                        const MediaSegment& aQueuedMedia,
                                        MediaStream* aInputStream,
                                        TrackID aInputTrackID)
 {
   if (!mDirectConnected) {
     NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, aQueuedMedia);
   } else {
-    if (aTrackEvents != 0) {
+    if (aTrackEvents != TrackEventCommand::TRACK_EVENT_NONE) {
       // forward events (TRACK_EVENT_ENDED) but not the media
       if (aQueuedMedia.GetType() == MediaSegment::VIDEO) {
         VideoSegment segment;
         NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, segment);
       } else {
         AudioSegment segment;
         NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, segment);
       }
@@ -120,17 +120,17 @@ MediaEncoder::NotifyQueuedAudioData(Medi
         mSuspended = RECORD_NOT_SUSPENDED; // no video
       }
     }
   }
 }
 
 void
 MediaEncoder::NotifyEvent(MediaStreamGraph* aGraph,
-                          MediaStreamListener::MediaStreamGraphEvent event)
+                          MediaStreamGraphEvent event)
 {
   // In case that MediaEncoder does not receive a TRACK_EVENT_ENDED event.
   LOG(LogLevel::Debug, ("NotifyRemoved in [MediaEncoder]."));
   if (mAudioEncoder) {
     mAudioEncoder->NotifyEvent(aGraph, event);
   }
   if (mVideoEncoder) {
     mVideoEncoder->NotifyEvent(aGraph, event);
--- a/dom/media/encoder/MediaEncoder.h
+++ b/dom/media/encoder/MediaEncoder.h
@@ -5,16 +5,17 @@
 
 #ifndef MediaEncoder_h_
 #define MediaEncoder_h_
 
 #include "mozilla/DebugOnly.h"
 #include "TrackEncoder.h"
 #include "ContainerWriter.h"
 #include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
 #include "nsAutoPtr.h"
 #include "nsIMemoryReporter.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Atomics.h"
 
 namespace mozilla {
 
 /**
@@ -120,17 +121,17 @@ public :
                           const MediaSegment& aRealtimeMedia) override;
 
   /**
    * Notified by the control loop of MediaStreamGraph; aQueueMedia is the raw
    * track data in form of MediaSegment.
    */
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                 StreamTime aTrackOffset,
-                                uint32_t aTrackEvents,
+                                TrackEventCommand aTrackEvents,
                                 const MediaSegment& aQueuedMedia,
                                 MediaStream* aInputStream,
                                 TrackID aInputTrackID) override;
 
   /**
    * Notifed by the control loop of MediaStreamGraph; aQueueMedia is the audio
    * data in the form of an AudioSegment.
    */
@@ -139,17 +140,17 @@ public :
                              const AudioSegment& aQueuedMedia,
                              MediaStream* aInputStream,
                              TrackID aInputTrackID) override;
 
   /**
    * * Notified the stream is being removed.
    */
   void NotifyEvent(MediaStreamGraph* aGraph,
-                   MediaStreamListener::MediaStreamGraphEvent event) override;
+                   MediaStreamGraphEvent event) override;
 
   /**
    * Creates an encoder with a given MIME type. Returns null if we are unable
    * to create the encoder. For now, default aMIMEType to "audio/ogg" and use
    * Ogg+Opus if it is empty.
    */
   static already_AddRefed<MediaEncoder> CreateEncoder(const nsAString& aMIMEType,
                                                       uint32_t aAudioBitrate, uint32_t aVideoBitrate,
--- a/dom/media/encoder/TrackEncoder.cpp
+++ b/dom/media/encoder/TrackEncoder.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* 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/. */
 #include "TrackEncoder.h"
 #include "AudioChannelFormat.h"
 #include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
 #include "mozilla/Logging.h"
 #include "VideoUtils.h"
 
 #undef LOG
 #ifdef MOZ_WIDGET_GONK
 #include <android/log.h>
 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaEncoder", ## args);
 #else
@@ -36,16 +37,24 @@ TrackEncoder::TrackEncoder()
   , mInitialized(false)
   , mEndOfStream(false)
   , mCanceled(false)
   , mInitCounter(0)
   , mNotInitDuration(0)
 {
 }
 
+void TrackEncoder::NotifyEvent(MediaStreamGraph* aGraph,
+                 MediaStreamGraphEvent event)
+{
+  if (event == MediaStreamGraphEvent::EVENT_REMOVED) {
+    NotifyEndOfStream();
+  }
+}
+
 void
 AudioTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
                                             TrackID aID,
                                             StreamTime aTrackOffset,
                                             uint32_t aTrackEvents,
                                             const MediaSegment& aQueuedMedia)
 {
   if (mCanceled) {
@@ -86,17 +95,17 @@ AudioTrackEncoder::NotifyQueuedTrackChan
     }
   }
 
   // Append and consume this raw segment.
   AppendAudioSegment(audio);
 
 
   // The stream has stopped and reached the end of track.
-  if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) {
+  if (aTrackEvents == TrackEventCommand::TRACK_EVENT_ENDED) {
     LOG("[AudioTrackEncoder]: Receive TRACK_EVENT_ENDED .");
     NotifyEndOfStream();
   }
 }
 
 void
 AudioTrackEncoder::NotifyEndOfStream()
 {
@@ -227,17 +236,17 @@ VideoTrackEncoder::NotifyQueuedTrackChan
       NotifyEndOfStream();
       return;
     }
   }
 
   AppendVideoSegment(video);
 
   // The stream has stopped and reached the end of track.
-  if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) {
+  if (aTrackEvents == TrackEventCommand::TRACK_EVENT_ENDED) {
     LOG("[VideoTrackEncoder]: Receive TRACK_EVENT_ENDED .");
     NotifyEndOfStream();
   }
 
 }
 
 nsresult
 VideoTrackEncoder::AppendVideoSegment(const VideoSegment& aSegment)
--- a/dom/media/encoder/TrackEncoder.h
+++ b/dom/media/encoder/TrackEncoder.h
@@ -43,22 +43,17 @@ public:
                                         uint32_t aTrackEvents,
                                         const MediaSegment& aQueuedMedia) = 0;
 
   /**
    * Notified by the same callback of MediaEncoder when it has been removed from
    * MediaStreamGraph. Called on the MediaStreamGraph thread.
    */
   void NotifyEvent(MediaStreamGraph* aGraph,
-                   MediaStreamListener::MediaStreamGraphEvent event)
-  {
-    if (event == MediaStreamListener::MediaStreamGraphEvent::EVENT_REMOVED) {
-      NotifyEndOfStream();
-    }
-  }
+                   MediaStreamGraphEvent event);
 
   /**
    * Creates and sets up meta data for a specific codec, called on the worker
    * thread.
    */
   virtual already_AddRefed<TrackMetadataBase> GetMetadata() = 0;
 
   /**
--- a/dom/media/gtest/TestVideoTrackEncoder.cpp
+++ b/dom/media/gtest/TestVideoTrackEncoder.cpp
@@ -4,16 +4,17 @@
 
 #include "gtest/gtest.h"
 #include <algorithm>
 
 #include "mozilla/ArrayUtils.h"
 #include "VP8TrackEncoder.h"
 #include "ImageContainer.h"
 #include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
 #include "WebMWriter.h" // TODO: it's weird to include muxer header to get the class definition of VP8 METADATA
 
 using ::testing::TestWithParam;
 using ::testing::Values;
 
 using namespace mozilla::layers;
 using namespace mozilla;
 
@@ -291,16 +292,16 @@ TEST(VP8VideoTrackEncoder, EncodeComplet
 {
   // Initiate VP8 encoder
   TestVP8TrackEncoder encoder;
   InitParam param = {true, 640, 480, 90000};
   encoder.TestInit(param);
 
   // track end notification.
   VideoSegment segment;
-  encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, MediaStreamListener::TRACK_EVENT_ENDED, segment);
+  encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, TrackEventCommand::TRACK_EVENT_ENDED, segment);
 
   // Pull Encoded Data back from encoder. Since we have sent
   // EOS to encoder, encoder.GetEncodedTrack should return
   // NS_OK immidiately.
   EncodedFrameContainer container;
   EXPECT_TRUE(NS_SUCCEEDED(encoder.GetEncodedTrack(container)));
 }
--- a/dom/media/imagecapture/CaptureTask.h
+++ b/dom/media/imagecapture/CaptureTask.h
@@ -3,16 +3,17 @@
 /* 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 CAPTURETASK_H
 #define CAPTURETASK_H
 
 #include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
 #include "PrincipalChangeObserver.h"
 
 namespace mozilla {
 
 namespace dom {
 class Blob;
 class ImageCapture;
 class MediaStreamTrack;
--- a/dom/media/mediasink/DecodedStream.cpp
+++ b/dom/media/mediasink/DecodedStream.cpp
@@ -8,16 +8,17 @@
 #include "mozilla/gfx/Point.h"
 #include "mozilla/SyncRunnable.h"
 
 #include "AudioSegment.h"
 #include "DecodedStream.h"
 #include "MediaData.h"
 #include "MediaQueue.h"
 #include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
 #include "OutputStreamManager.h"
 #include "SharedBuffer.h"
 #include "VideoSegment.h"
 #include "VideoUtils.h"
 
 namespace mozilla {
 
 /*
@@ -25,17 +26,16 @@ namespace mozilla {
  * way to DecodedStreamGraphListener from DecodedStream.
  */
 struct PlaybackInfoInit {
   int64_t mStartTime;
   MediaInfo mInfo;
 };
 
 class DecodedStreamGraphListener : public MediaStreamListener {
-  typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent;
 public:
   DecodedStreamGraphListener(MediaStream* aStream,
                              MozPromiseHolder<GenericPromise>&& aPromise)
     : mMutex("DecodedStreamGraphListener::mMutex")
     , mStream(aStream)
     , mLastOutputTime(aStream->StreamTimeToMicroseconds(aStream->GetCurrentTime()))
   {
     mFinishPromise = Move(aPromise);
@@ -47,17 +47,17 @@ public:
     if (mStream) {
       mLastOutputTime = mStream->StreamTimeToMicroseconds(
           mStream->GraphTimeToStreamTime(aCurrentTime));
     }
   }
 
   void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override
   {
-    if (event == EVENT_FINISHED) {
+    if (event == MediaStreamGraphEvent::EVENT_FINISHED) {
       nsCOMPtr<nsIRunnable> event =
         NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
     }
   }
 
   void DoNotifyFinished()
   {
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -122,16 +122,17 @@ EXPORTS += [
     'MediaPrefs.h',
     'MediaQueue.h',
     'MediaRecorder.h',
     'MediaResource.h',
     'MediaResourceCallback.h',
     'MediaSegment.h',
     'MediaStatistics.h',
     'MediaStreamGraph.h',
+    'MediaStreamListener.h',
     'MediaTimer.h',
     'MediaTrack.h',
     'MediaTrackList.h',
     'MP3Decoder.h',
     'MP3Demuxer.h',
     'MP3FrameParser.h',
     'NextFrameSeekTask.h',
     'nsIDocumentActivity.h',
@@ -233,16 +234,17 @@ UNIFIED_SOURCES += [
     'MediaInfo.cpp',
     'MediaManager.cpp',
     'MediaPrefs.cpp',
     'MediaRecorder.cpp',
     'MediaResource.cpp',
     'MediaShutdownManager.cpp',
     'MediaStreamError.cpp',
     'MediaStreamGraph.cpp',
+    'MediaStreamListener.cpp',
     'MediaStreamTrack.cpp',
     'MediaTimer.cpp',
     'MediaTrack.cpp',
     'MediaTrackList.cpp',
     'MP3Decoder.cpp',
     'MP3Demuxer.cpp',
     'MP3FrameParser.cpp',
     'NextFrameSeekTask.cpp',
--- a/dom/media/webaudio/AudioNodeStream.cpp
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* 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/. */
 
 #include "AudioNodeStream.h"
 
 #include "MediaStreamGraphImpl.h"
+#include "MediaStreamListener.h"
 #include "AudioNodeEngine.h"
 #include "ThreeDPoint.h"
 #include "AudioChannelFormat.h"
 #include "AudioParamTimeline.h"
 #include "AudioContext.h"
 #include "nsMathUtils.h"
 
 using namespace mozilla::dom;
@@ -642,32 +643,32 @@ AudioNodeStream::AdvanceOutputSegment()
   }
 
   for (uint32_t j = 0; j < mListeners.Length(); ++j) {
     MediaStreamListener* l = mListeners[j];
     AudioChunk copyChunk = mLastChunks[0].AsAudioChunk();
     AudioSegment tmpSegment;
     tmpSegment.AppendAndConsumeChunk(&copyChunk);
     l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
-                                segment->GetDuration(), 0, tmpSegment);
+                                segment->GetDuration(), TrackEventCommand::TRACK_EVENT_NONE, tmpSegment);
   }
 }
 
 void
 AudioNodeStream::FinishOutput()
 {
   StreamTracks::Track* track = EnsureTrack(AUDIO_TRACK);
   track->SetEnded();
 
   for (uint32_t j = 0; j < mListeners.Length(); ++j) {
     MediaStreamListener* l = mListeners[j];
     AudioSegment emptySegment;
     l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
                                 track->GetSegment()->GetDuration(),
-                                MediaStreamListener::TRACK_EVENT_ENDED, emptySegment);
+                                TrackEventCommand::TRACK_EVENT_ENDED, emptySegment);
   }
 }
 
 void
 AudioNodeStream::AddInput(MediaInputPort* aPort)
 {
   ProcessedMediaStream::AddInput(aPort);
   AudioNodeStream* ns = aPort->GetSource()->AsAudioNodeStream();
--- a/dom/media/webspeech/recognition/SpeechStreamListener.cpp
+++ b/dom/media/webspeech/recognition/SpeechStreamListener.cpp
@@ -80,15 +80,15 @@ SpeechStreamListener::ConvertAndDispatch
   int16_t* to = static_cast<int16_t*>(samples->Data());
   ConvertAudioSamplesWithScale(aData, to, aDuration, aVolume);
 
   mRecognition->FeedAudioData(samples.forget(), aDuration, this, aTrackRate);
 }
 
 void
 SpeechStreamListener::NotifyEvent(MediaStreamGraph* aGraph,
-                                  MediaStreamListener::MediaStreamGraphEvent event)
+                                  MediaStreamGraphEvent event)
 {
   // TODO dispatch SpeechEnd event so services can be informed
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webspeech/recognition/SpeechStreamListener.h
+++ b/dom/media/webspeech/recognition/SpeechStreamListener.h
@@ -3,16 +3,17 @@
 /* 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_dom_SpeechStreamListener_h
 #define mozilla_dom_SpeechStreamListener_h
 
 #include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
 #include "AudioSegment.h"
 
 namespace mozilla {
 
 class AudioSegment;
 
 namespace dom {
 
@@ -26,17 +27,17 @@ public:
 
   void NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
                              StreamTime aTrackOffset,
                              const AudioSegment& aQueuedMedia,
                              MediaStream* aInputStream,
                              TrackID aInputTrackID) override;
 
   void NotifyEvent(MediaStreamGraph* aGraph,
-                   MediaStreamListener::MediaStreamGraphEvent event) override;
+                   MediaStreamGraphEvent event) override;
 
 private:
   template<typename SampleFormatType>
   void ConvertAndDispatchAudioChunk(int aDuration, float aVolume, SampleFormatType* aData, TrackRate aTrackRate);
   RefPtr<SpeechRecognition> mRecognition;
 };
 
 } // namespace dom
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp
+++ b/dom/media/webspeech/synth/nsSpeechTask.cpp
@@ -2,18 +2,20 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/. */
 
 #include "AudioChannelAgent.h"
 #include "AudioChannelService.h"
 #include "AudioSegment.h"
+#include "MediaStreamListener.h"
 #include "nsSpeechTask.h"
 #include "nsSynthVoiceRegistry.h"
+#include "SharedBuffer.h"
 #include "SpeechSynthesis.h"
 
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
 // GetTickCount() and conflicts with nsSpeechTask::GetCurrentTime().
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 
@@ -48,34 +50,34 @@ public:
   {
     if (mSpeechTask) {
       mSpeechTask->DispatchEndInner(mSpeechTask->GetCurrentTime(),
                                     mSpeechTask->GetCurrentCharOffset());
     }
   }
 
   void NotifyEvent(MediaStreamGraph* aGraph,
-                   MediaStreamListener::MediaStreamGraphEvent event) override
+                   MediaStreamGraphEvent event) override
   {
     switch (event) {
-      case EVENT_FINISHED:
+      case MediaStreamGraphEvent::EVENT_FINISHED:
         {
           if (!mStarted) {
             mStarted = true;
             nsCOMPtr<nsIRunnable> startRunnable =
               NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted);
             aGraph->DispatchToMainThreadAfterStreamStateUpdate(startRunnable.forget());
           }
 
           nsCOMPtr<nsIRunnable> endRunnable =
             NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished);
           aGraph->DispatchToMainThreadAfterStreamStateUpdate(endRunnable.forget());
         }
         break;
-      case EVENT_REMOVED:
+      case MediaStreamGraphEvent::EVENT_REMOVED:
         mSpeechTask = nullptr;
         // Dereference MediaStream to destroy safety
         mStream = nullptr;
         break;
       default:
         break;
     }
   }
--- a/dom/media/webspeech/synth/nsSpeechTask.h
+++ b/dom/media/webspeech/synth/nsSpeechTask.h
@@ -8,16 +8,19 @@
 #define mozilla_dom_nsSpeechTask_h
 
 #include "MediaStreamGraph.h"
 #include "SpeechSynthesisUtterance.h"
 #include "nsIAudioChannelAgent.h"
 #include "nsISpeechService.h"
 
 namespace mozilla {
+
+class SharedBuffer;
+
 namespace dom {
 
 class SpeechSynthesisUtterance;
 class SpeechSynthesis;
 class SynthStreamListener;
 
 class nsSpeechTask : public nsISpeechTask
                    , public nsIAudioChannelAgentCallback
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
@@ -19,16 +19,17 @@
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
 #include "VideoSegment.h"
 #include "Layers.h"
 #include "LayersLogging.h"
 #include "ImageTypes.h"
 #include "ImageContainer.h"
 #include "DOMMediaStream.h"
 #include "MediaStreamTrack.h"
+#include "MediaStreamListener.h"
 #include "VideoUtils.h"
 #ifdef WEBRTC_GONK
 #include "GrallocImages.h"
 #include "mozilla/layers/GrallocTextureClient.h"
 #endif
 #endif
 
 #include "nsError.h"
--- a/media/webrtc/signaling/test/FakeMediaStreams.h
+++ b/media/webrtc/signaling/test/FakeMediaStreams.h
@@ -45,16 +45,19 @@ class MediaStreamGraph;
 static MediaStreamGraph* gGraph;
 
 struct AudioChannel {
   enum {
     Normal
   };
 };
 
+enum MediaStreamGraphEvent : uint32_t;
+enum TrackEventCommand : uint32_t;
+
 class MediaStreamGraph {
 public:
   // Keep this in sync with the enum in MediaStreamGraph.h
   enum GraphDriverType {
     AUDIO_THREAD_DRIVER,
     SYSTEM_THREAD_DRIVER,
     OFFLINE_THREAD_DRIVER
   };
@@ -84,17 +87,17 @@ class Fake_SourceMediaStream;
 class Fake_MediaStreamListener
 {
 protected:
   virtual ~Fake_MediaStreamListener() {}
 
 public:
   virtual void NotifyQueuedTrackChanges(mozilla::MediaStreamGraph* aGraph, mozilla::TrackID aID,
                                         mozilla::StreamTime aTrackOffset,
-                                        uint32_t aTrackEvents,
+                                        mozilla::TrackEventCommand  aTrackEvents,
                                         const mozilla::MediaSegment& aQueuedMedia,
                                         Fake_MediaStream* aInputStream,
                                         mozilla::TrackID aInputTrackID) {}
   virtual void NotifyPull(mozilla::MediaStreamGraph* aGraph, mozilla::StreamTime aDesiredTime) = 0;
   virtual void NotifyQueuedAudioData(mozilla::MediaStreamGraph* aGraph, mozilla::TrackID aID,
                                        mozilla::StreamTime aTrackOffset,
                                        const mozilla::AudioSegment& aQueuedMedia,
                                        Fake_MediaStream* aInputStream,
--- a/media/webrtc/signaling/test/FakeMediaStreamsImpl.h
+++ b/media/webrtc/signaling/test/FakeMediaStreamsImpl.h
@@ -123,17 +123,17 @@ void Fake_AudioStreamSource::Periodic() 
                        AUDIO_BUFFER_SIZE,
                        PRINCIPAL_HANDLE_NONE);
 
   for(std::set<RefPtr<Fake_MediaStreamListener>>::iterator it = mListeners.begin();
        it != mListeners.end(); ++it) {
     (*it)->NotifyQueuedTrackChanges(nullptr, // Graph
                                     0, // TrackID
                                     0, // Offset TODO(ekr@rtfm.com) fix
-                                    0, // ???
+                                    static_cast<mozilla::TrackEventCommand>(0), // ???
                                     segment,
                                     nullptr, // Input stream
                                     -1);     // Input track id
   }
   for(std::vector<BoundTrackListener>::iterator it = mTrackListeners.begin();
        it != mTrackListeners.end(); ++it) {
     it->mListener->NotifyQueuedChanges(nullptr, // Graph
                                        0, // Offset TODO(ekr@rtfm.com) fix