Bug 1035819: patch 1 - Add notification of DirectListeners and generalize Notification of events r=roc
authorRandell Jesup <rjesup@jesup.org>
Mon, 14 Jul 2014 01:47:56 -0400
changeset 215676 e1783fc9c59a1c852c665f7d6ee14982499f4432
parent 215675 12b60cc85be1719322b6d1ee5efd6fd8d826c336
child 215677 39f5872c10e7aea993f89d6c69374d67d0ad4dac
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1035819
milestone33.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 1035819: patch 1 - Add notification of DirectListeners and generalize Notification of events r=roc
content/html/content/src/HTMLMediaElement.cpp
content/media/MediaDecoder.cpp
content/media/MediaDecoder.h
content/media/MediaStreamGraph.cpp
content/media/MediaStreamGraph.h
content/media/encoder/MediaEncoder.cpp
content/media/encoder/MediaEncoder.h
content/media/encoder/TrackEncoder.h
content/media/webspeech/recognition/SpeechStreamListener.cpp
content/media/webspeech/recognition/SpeechStreamListener.h
content/media/webspeech/synth/nsSpeechTask.cpp
dom/camera/CameraPreviewMediaStream.cpp
dom/media/MediaManager.h
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -2763,21 +2763,24 @@ public:
     nsCOMPtr<nsIRunnable> event;
     if (aBlocked == BLOCKED) {
       event = NS_NewRunnableMethod(this, &StreamListener::DoNotifyBlocked);
     } else {
       event = NS_NewRunnableMethod(this, &StreamListener::DoNotifyUnblocked);
     }
     aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
   }
-  virtual void NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE
+  virtual void NotifyEvent(MediaStreamGraph* aGraph,
+                           MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE
   {
-    nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethod(this, &StreamListener::DoNotifyFinished);
-    aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+    if (event == EVENT_FINISHED) {
+      nsCOMPtr<nsIRunnable> event =
+        NS_NewRunnableMethod(this, &StreamListener::DoNotifyFinished);
+      aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+    }
   }
   virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) MOZ_OVERRIDE
   {
     MutexAutoLock lock(mMutex);
     nsCOMPtr<nsIRunnable> event =
       NS_NewRunnableMethod(this, &StreamListener::DoNotifyHaveCurrentData);
     aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
   }
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -256,23 +256,25 @@ MediaDecoder::DecodedStreamGraphListener
     }
   }
 
   MutexAutoLock lock(mMutex);
   mStreamFinishedOnMainThread = true;
 }
 
 void
-MediaDecoder::DecodedStreamGraphListener::NotifyFinished(MediaStreamGraph* aGraph)
+MediaDecoder::DecodedStreamGraphListener::NotifyEvent(MediaStreamGraph* aGraph,
+  MediaStreamListener::MediaStreamGraphEvent event)
 {
-  nsCOMPtr<nsIRunnable> event =
-    NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
-  aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+  if (event == EVENT_FINISHED) {
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
+    aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+  }
 }
-
 void MediaDecoder::DestroyDecodedStream()
 {
   MOZ_ASSERT(NS_IsMainThread());
   GetReentrantMonitor().AssertCurrentThreadIn();
 
   // All streams are having their SourceMediaStream disconnected, so they
   // need to be explicitly blocked again.
   for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -435,17 +435,18 @@ public:
     // mDecoderStateMachine is non-null and MediaDecoderStateMachine is false.
     bool mHaveBlockedForStateMachineNotPlaying;
   };
 
   class DecodedStreamGraphListener : public MediaStreamListener {
   public:
     DecodedStreamGraphListener(MediaStream* aStream, DecodedStreamData* aData);
     virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) MOZ_OVERRIDE;
-    virtual void NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
+    virtual void NotifyEvent(MediaStreamGraph* aGraph,
+                             MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE;
 
     void DoNotifyFinished();
 
     int64_t GetLastOutputTime() // microseconds
     {
       MutexAutoLock lock(mMutex);
       return mLastOutputTime;
     }
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -392,17 +392,17 @@ MediaStreamGraphImpl::UpdateCurrentTime(
   streamHasOutput.SetLength(mStreams.Length());
   for (uint32_t i = 0; i < mStreams.Length(); ++i) {
     MediaStream* stream = mStreams[i];
 
     // Calculate blocked time and fire Blocked/Unblocked events
     GraphTime blockedTime = 0;
     GraphTime t = prevCurrentTime;
     // include |nextCurrentTime| to ensure NotifyBlockingChanged() is called
-    // before NotifyFinished() when |nextCurrentTime == stream end time|
+    // before NotifyEvent(this, EVENT_FINISHED) when |nextCurrentTime == stream end time|
     while (t <= nextCurrentTime) {
       GraphTime end;
       bool blocked = stream->mBlocked.GetAt(t, &end);
       if (blocked) {
         blockedTime += std::min(end, nextCurrentTime) - t;
       }
       if (blocked != stream->mNotifiedBlocked) {
         for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
@@ -455,17 +455,17 @@ MediaStreamGraphImpl::UpdateCurrentTime(
           stream->StreamTimeToGraphTime(stream->GetStreamBuffer().GetAllTracksEnd()))  {
       NS_WARN_IF_FALSE(stream->mNotifiedBlocked,
         "Should've notified blocked=true for a fully finished stream");
       stream->mNotifiedFinished = true;
       stream->mLastPlayedVideoFrame.SetNull();
       SetStreamOrderDirty();
       for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
         MediaStreamListener* l = stream->mListeners[j];
-        l->NotifyFinished(this);
+        l->NotifyEvent(this, MediaStreamListener::EVENT_FINISHED);
       }
     }
   }
 }
 
 bool
 MediaStreamGraphImpl::WillUnderrun(MediaStream* aStream, GraphTime aTime,
                                    GraphTime aEndBlockingDecisions, GraphTime* aEnd)
@@ -1928,17 +1928,17 @@ MediaStream::EnsureTrack(TrackID aTrackI
   return track;
 }
 
 void
 MediaStream::RemoveAllListenersImpl()
 {
   for (int32_t i = mListeners.Length() - 1; i >= 0; --i) {
     nsRefPtr<MediaStreamListener> listener = mListeners[i].forget();
-    listener->NotifyRemoved(GraphImpl());
+    listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
   }
   mListeners.Clear();
 }
 
 void
 MediaStream::DestroyImpl()
 {
   RemoveAllListenersImpl();
@@ -2106,17 +2106,17 @@ MediaStream::ChangeExplicitBlockerCount(
 
 void
 MediaStream::AddListenerImpl(already_AddRefed<MediaStreamListener> aListener)
 {
   MediaStreamListener* listener = *mListeners.AppendElement() = aListener;
   listener->NotifyBlockingChanged(GraphImpl(),
     mNotifiedBlocked ? MediaStreamListener::BLOCKED : MediaStreamListener::UNBLOCKED);
   if (mNotifiedFinished) {
-    listener->NotifyFinished(GraphImpl());
+    listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_FINISHED);
   }
   if (mNotifiedHasCurrentData) {
     listener->NotifyHasCurrentData(GraphImpl());
   }
 }
 
 void
 MediaStream::AddListener(MediaStreamListener* aListener)
@@ -2135,17 +2135,17 @@ MediaStream::AddListener(MediaStreamList
 }
 
 void
 MediaStream::RemoveListenerImpl(MediaStreamListener* aListener)
 {
   // wouldn't need this if we could do it in the opposite order
   nsRefPtr<MediaStreamListener> listener(aListener);
   mListeners.RemoveElement(aListener);
-  listener->NotifyRemoved(GraphImpl());
+  listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
 }
 
 void
 MediaStream::RemoveListener(MediaStreamListener* aListener)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, MediaStreamListener* aListener) :
@@ -2362,25 +2362,47 @@ SourceMediaStream::NotifyDirectConsumers
     l->NotifyRealtimeData(static_cast<MediaStreamGraph*>(GraphImpl()), aTrack->mID, aTrack->mOutputRate,
                           offset, aTrack->mCommands, *aSegment);
   }
 }
 
 void
 SourceMediaStream::AddDirectListener(MediaStreamDirectListener* aListener)
 {
-  MutexAutoLock lock(mMutex);
-  mDirectListeners.AppendElement(aListener);
+  bool wasEmpty;
+  {
+    MutexAutoLock lock(mMutex);
+    wasEmpty = mDirectListeners.IsEmpty();
+    mDirectListeners.AppendElement(aListener);
+  }
+
+  if (wasEmpty) {
+    for (uint32_t j = 0; j < mListeners.Length(); ++j) {
+      MediaStreamListener* l = mListeners[j];
+      l->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_HAS_DIRECT_LISTENERS);
+    }
+  }
 }
 
 void
 SourceMediaStream::RemoveDirectListener(MediaStreamDirectListener* aListener)
 {
-  MutexAutoLock lock(mMutex);
-  mDirectListeners.RemoveElement(aListener);
+  bool isEmpty;
+  {
+    MutexAutoLock lock(mMutex);
+    mDirectListeners.RemoveElement(aListener);
+    isEmpty = mDirectListeners.IsEmpty();
+  }
+
+  if (isEmpty) {
+    for (uint32_t j = 0; j < mListeners.Length(); ++j) {
+      MediaStreamListener* l = mListeners[j];
+      l->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_HAS_NO_DIRECT_LISTENERS);
+    }
+  }
 }
 
 bool
 SourceMediaStream::HaveEnoughBuffered(TrackID aID)
 {
   MutexAutoLock lock(mMutex);
   TrackData *track = FindDataForTrack(aID);
   if (track) {
@@ -2450,17 +2472,17 @@ void
 SourceMediaStream::EndAllTrackAndFinish()
 {
   MutexAutoLock lock(mMutex);
   for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
     SourceMediaStream::TrackData* data = &mUpdateTracks[i];
     data->mCommands |= TRACK_END;
   }
   FinishWithLockHeld();
-  // we will call NotifyFinished() to let GetUserMedia know
+  // we will call NotifyEvent() to let GetUserMedia know
 }
 
 TrackTicks
 SourceMediaStream::GetBufferedTicks(TrackID aID)
 {
   StreamBuffer::Track* track  = mBuffer.FindTrack(aID);
   if (track) {
     MediaSegment* segment = track->GetSegment();
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -106,16 +106,17 @@ protected:
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener)
 
   enum Consumption {
     CONSUMED,
     NOT_CONSUMED
   };
+
   /**
    * Notify that the stream is hooked up and we'd like to start or stop receiving
    * data on it. Only fires on SourceMediaStreams.
    * The initial state is assumed to be NOT_CONSUMED.
    */
   virtual void NotifyConsumptionChanged(MediaStreamGraph* aGraph, Consumption aConsuming) {}
 
   /**
@@ -152,26 +153,27 @@ public:
 
   /**
    * 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 the stream finished.
-   */
-  virtual void NotifyFinished(MediaStreamGraph* aGraph) {}
+  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 your listener has been removed, either due to RemoveListener(),
-   * or due to the stream being destroyed.  You will get no further notifications.
+   * Notify that an event has occurred on the Stream
    */
-  virtual void NotifyRemoved(MediaStreamGraph* aGraph) {}
+  virtual void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent aEvent) {}
 
   enum {
     TRACK_EVENT_CREATED = 0x01,
     TRACK_EVENT_ENDED = 0x02
   };
   /**
    * Notify that changes to one of the stream tracks have been queued.
    * aTrackEvents can be any combination of TRACK_EVENT_CREATED and
--- a/content/media/encoder/MediaEncoder.cpp
+++ b/content/media/encoder/MediaEncoder.cpp
@@ -59,27 +59,27 @@ MediaEncoder::NotifyQueuedTrackChanges(M
   } else if (mVideoEncoder && aQueuedMedia.GetType() == MediaSegment::VIDEO) {
       mVideoEncoder->NotifyQueuedTrackChanges(aGraph, aID, aTrackRate,
                                               aTrackOffset, aTrackEvents,
                                               aQueuedMedia);
   }
 }
 
 void
-MediaEncoder::NotifyRemoved(MediaStreamGraph* aGraph)
+MediaEncoder::NotifyEvent(MediaStreamGraph* aGraph,
+                          MediaStreamListener::MediaStreamGraphEvent event)
 {
   // In case that MediaEncoder does not receive a TRACK_EVENT_ENDED event.
   LOG(PR_LOG_DEBUG, ("NotifyRemoved in [MediaEncoder]."));
   if (mAudioEncoder) {
-    mAudioEncoder->NotifyRemoved(aGraph);
+    mAudioEncoder->NotifyEvent(aGraph, event);
   }
   if (mVideoEncoder) {
-    mVideoEncoder->NotifyRemoved(aGraph);
+    mVideoEncoder->NotifyEvent(aGraph, event);
   }
-
 }
 
 /* static */
 already_AddRefed<MediaEncoder>
 MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes)
 {
 #ifdef PR_LOGGING
   if (!gMediaEncoderLog) {
--- a/content/media/encoder/MediaEncoder.h
+++ b/content/media/encoder/MediaEncoder.h
@@ -75,22 +75,23 @@ public :
   /**
    * Notified by the control loop of MediaStreamGraph; aQueueMedia is the raw
    * track data in form of MediaSegment.
    */
   virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                         TrackRate aTrackRate,
                                         TrackTicks aTrackOffset,
                                         uint32_t aTrackEvents,
-                                        const MediaSegment& aQueuedMedia);
+                                        const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
 
   /**
    * Notified the stream is being removed.
    */
-  virtual void NotifyRemoved(MediaStreamGraph* aGraph);
+  virtual void NotifyEvent(MediaStreamGraph* aGraph,
+                           MediaStreamListener::MediaStreamGraphEvent event) MOZ_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,
                                                       uint8_t aTrackTypes = ContainerWriter::CREATE_AUDIO_TRACK);
--- a/content/media/encoder/TrackEncoder.h
+++ b/content/media/encoder/TrackEncoder.h
@@ -8,21 +8,20 @@
 
 #include "mozilla/ReentrantMonitor.h"
 
 #include "AudioSegment.h"
 #include "EncodedFrameContainer.h"
 #include "StreamBuffer.h"
 #include "TrackMetadataBase.h"
 #include "VideoSegment.h"
+#include "MediaStreamGraph.h"
 
 namespace mozilla {
 
-class MediaStreamGraph;
-
 /**
  * Base class of AudioTrackEncoder and VideoTrackEncoder. Lifetimes managed by
  * MediaEncoder. Most methods can only be called on the MediaEncoder's thread,
  * but some subclass methods can be called on other threads when noted.
  *
  * NotifyQueuedTrackChanges is called on subclasses of this class from the
  * MediaStreamGraph thread, and AppendAudioSegment/AppendVideoSegment is then
  * called to store media data in the TrackEncoder. Later on, GetEncodedTrack is
@@ -44,17 +43,23 @@ public:
                                         TrackTicks aTrackOffset,
                                         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 NotifyRemoved(MediaStreamGraph* aGraph) { NotifyEndOfStream(); }
+  void NotifyEvent(MediaStreamGraph* aGraph,
+                   MediaStreamListener::MediaStreamGraphEvent event)
+  {
+    if (event == MediaStreamListener::MediaStreamGraphEvent::EVENT_REMOVED) {
+      NotifyEndOfStream();
+    }
+  }
 
   /**
    * Creates and sets up meta data for a specific codec, called on the worker
    * thread.
    */
   virtual already_AddRefed<TrackMetadataBase> GetMetadata() = 0;
 
   /**
--- a/content/media/webspeech/recognition/SpeechStreamListener.cpp
+++ b/content/media/webspeech/recognition/SpeechStreamListener.cpp
@@ -79,15 +79,16 @@ SpeechStreamListener::ConvertAndDispatch
 
   int16_t* to = static_cast<int16_t*>(samples->Data());
   ConvertAudioSamplesWithScale(aData, to, aDuration, aVolume);
 
   mRecognition->FeedAudioData(samples.forget(), aDuration, this);
 }
 
 void
-SpeechStreamListener::NotifyFinished(MediaStreamGraph* aGraph)
+SpeechStreamListener::NotifyEvent(MediaStreamGraph* aGraph,
+                                  MediaStreamListener::MediaStreamGraphEvent event)
 {
   // TODO dispatch SpeechEnd event so services can be informed
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/media/webspeech/recognition/SpeechStreamListener.h
+++ b/content/media/webspeech/recognition/SpeechStreamListener.h
@@ -25,17 +25,18 @@ public:
   ~SpeechStreamListener();
 
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                 TrackRate aTrackRate,
                                 TrackTicks aTrackOffset,
                                 uint32_t aTrackEvents,
                                 const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
 
-  void NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
+  void NotifyEvent(MediaStreamGraph* aGraph,
+                   MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE;
 
 private:
   template<typename SampleFormatType>
   void ConvertAndDispatchAudioChunk(int aDuration, float aVolume, SampleFormatType* aData);
   nsRefPtr<SpeechRecognition> mRecognition;
 };
 
 } // namespace dom
--- a/content/media/webspeech/synth/nsSpeechTask.cpp
+++ b/content/media/webspeech/synth/nsSpeechTask.cpp
@@ -44,38 +44,45 @@ public:
   void DoNotifyFinished()
   {
     if (mSpeechTask) {
       mSpeechTask->DispatchEndImpl(mSpeechTask->GetCurrentTime(),
                                    mSpeechTask->GetCurrentCharOffset());
     }
   }
 
-  virtual void NotifyFinished(MediaStreamGraph* aGraph)
+  virtual void NotifyEvent(MediaStreamGraph* aGraph,
+                           MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE
   {
-    nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished);
-    aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+    switch (event) {
+      case EVENT_FINISHED:
+        {
+          nsCOMPtr<nsIRunnable> runnable =
+            NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished);
+          aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
+        }
+        break;
+      case EVENT_REMOVED:
+        mSpeechTask = nullptr;
+        break;
+      default:
+        break;
+    }
   }
 
   virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked)
   {
     if (aBlocked == MediaStreamListener::UNBLOCKED && !mStarted) {
       mStarted = true;
       nsCOMPtr<nsIRunnable> event =
         NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
     }
   }
 
-  virtual void NotifyRemoved(MediaStreamGraph* aGraph)
-  {
-    mSpeechTask = nullptr;
-  }
-
 private:
   // Raw pointer; if we exist, the stream exists,
   // and 'mSpeechTask' exclusively owns it and therefor exists as well.
   nsSpeechTask* mSpeechTask;
 
   bool mStarted;
 };
 
--- a/dom/camera/CameraPreviewMediaStream.cpp
+++ b/dom/camera/CameraPreviewMediaStream.cpp
@@ -98,17 +98,17 @@ CameraPreviewMediaStream::AddListener(Me
 void
 CameraPreviewMediaStream::RemoveListener(MediaStreamListener* aListener)
 {
   MutexAutoLock lock(mMutex);
 
   MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
   nsRefPtr<MediaStreamListener> listener(aListener);
   mListeners.RemoveElement(aListener);
-  listener->NotifyRemoved(gm);
+  listener->NotifyEvent(gm, MediaStreamListener::EVENT_REMOVED);
 }
 
 void
 CameraPreviewMediaStream::Destroy()
 {
   MutexAutoLock lock(mMutex);
   DestroyImpl();
 }
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -170,20 +170,38 @@ public:
       mAudioSource->NotifyPull(aGraph, mStream, kAudioTrack, aDesiredTime, mLastEndTimeAudio);
     }
     if (mVideoSource) {
       mVideoSource->NotifyPull(aGraph, mStream, kVideoTrack, aDesiredTime, mLastEndTimeVideo);
     }
   }
 
   virtual void
-  NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
+  NotifyEvent(MediaStreamGraph* aGraph,
+              MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE
+  {
+    switch (event) {
+      case EVENT_FINISHED:
+        NotifyFinished(aGraph);
+        break;
+      case EVENT_REMOVED:
+        NotifyRemoved(aGraph);
+        break;
+      case EVENT_HAS_DIRECT_LISTENERS:
+      case EVENT_HAS_NO_DIRECT_LISTENERS:
+        //NotifyListenerEvent(aGraph, event);
+        break;
+    }
+  }
 
   virtual void
-  NotifyRemoved(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
+  NotifyFinished(MediaStreamGraph* aGraph);
+
+  virtual void
+  NotifyRemoved(MediaStreamGraph* aGraph);
 
 private:
   // Set at construction
   nsCOMPtr<nsIThread> mMediaThread;
   uint64_t mWindowID;
 
   bool mStopped; // MainThread only