Bug 1208371 - Add a MediaStreamTrackListener to MediaStreamGraph. r?roc
MozReview-Commit-ID: 6KHzimw9kiP
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -174,22 +174,31 @@ MediaStreamGraphImpl::ExtractPendingInpu
}
}
}
finished = aStream->mUpdateFinished;
bool notifiedTrackCreated = false;
for (int32_t i = aStream->mUpdateTracks.Length() - 1; i >= 0; --i) {
SourceMediaStream::TrackData* data = &aStream->mUpdateTracks[i];
aStream->ApplyTrackDisabling(data->mID, data->mData);
+ StreamTime offset = (data->mCommands & SourceMediaStream::TRACK_CREATE)
+ ? data->mStart : aStream->mBuffer.FindTrack(data->mID)->GetSegment()->GetDuration();
for (MediaStreamListener* l : aStream->mListeners) {
- StreamTime offset = (data->mCommands & SourceMediaStream::TRACK_CREATE)
- ? data->mStart : aStream->mBuffer.FindTrack(data->mID)->GetSegment()->GetDuration();
l->NotifyQueuedTrackChanges(this, data->mID,
offset, data->mCommands, *data->mData);
}
+ for (TrackBound<MediaStreamTrackListener>& b : aStream->mTrackListeners) {
+ if (b.mTrackID != data->mID) {
+ continue;
+ }
+ b.mListener->NotifyQueuedChanges(this, offset, *data->mData);
+ if (data->mCommands & SourceMediaStream::TRACK_END) {
+ b.mListener->NotifyEnded();
+ }
+ }
if (data->mCommands & SourceMediaStream::TRACK_CREATE) {
MediaSegment* segment = data->mData.forget();
STREAM_LOG(LogLevel::Debug, ("SourceMediaStream %p creating track %d, start %lld, initial end %lld",
aStream, data->mID, int64_t(data->mStart),
int64_t(segment->GetDuration())));
data->mEndOfFlushedData += segment->GetDuration();
aStream->mBuffer.AddTrack(data->mID, data->mStart, segment);
@@ -2222,16 +2231,77 @@ MediaStream::RemoveListener(MediaStreamL
// If the stream is destroyed the Listeners have or will be
// removed.
if (!IsDestroyed()) {
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener));
}
}
void
+MediaStream::AddTrackListenerImpl(already_AddRefed<MediaStreamTrackListener> aListener,
+ TrackID aTrackID)
+{
+ TrackBound<MediaStreamTrackListener>* l = mTrackListeners.AppendElement();
+ l->mListener = aListener;
+ l->mTrackID = aTrackID;
+}
+
+void
+MediaStream::AddTrackListener(MediaStreamTrackListener* aListener,
+ TrackID aTrackID)
+{
+ class Message : public ControlMessage {
+ public:
+ Message(MediaStream* aStream, MediaStreamTrackListener* aListener,
+ TrackID aTrackID) :
+ ControlMessage(aStream), mListener(aListener), mTrackID(aTrackID) {}
+ virtual void Run()
+ {
+ mStream->AddTrackListenerImpl(mListener.forget(), mTrackID);
+ }
+ RefPtr<MediaStreamTrackListener> mListener;
+ TrackID mTrackID;
+ };
+ GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener, aTrackID));
+}
+
+void
+MediaStream::RemoveTrackListenerImpl(MediaStreamTrackListener* aListener,
+ TrackID aTrackID)
+{
+ for (size_t i = 0; i < mTrackListeners.Length(); ++i) {
+ if (mTrackListeners[i].mListener == aListener &&
+ mTrackListeners[i].mTrackID == aTrackID) {
+ mTrackListeners[i].mListener->NotifyRemoved();
+ mTrackListeners.RemoveElementAt(i);
+ return;
+ }
+ }
+}
+
+void
+MediaStream::RemoveTrackListener(MediaStreamTrackListener* aListener,
+ TrackID aTrackID)
+{
+ class Message : public ControlMessage {
+ public:
+ Message(MediaStream* aStream, MediaStreamTrackListener* aListener,
+ TrackID aTrackID) :
+ ControlMessage(aStream), mListener(aListener), mTrackID(aTrackID) {}
+ virtual void Run()
+ {
+ mStream->RemoveTrackListenerImpl(mListener, mTrackID);
+ }
+ RefPtr<MediaStreamTrackListener> mListener;
+ TrackID mTrackID;
+ };
+ GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener, aTrackID));
+}
+
+void
MediaStream::RunAfterPendingUpdates(already_AddRefed<nsIRunnable> aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
MediaStreamGraphImpl* graph = GraphImpl();
nsCOMPtr<nsIRunnable> runnable(aRunnable);
// Special case when a non-realtime graph has not started, to ensure the
// runnable will run in finite time.
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -212,16 +212,51 @@ 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 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 MediaStreamDirectListener : public MediaStreamListener
{
public:
@@ -274,16 +309,26 @@ class SourceMediaStream;
class ProcessedMediaStream;
class MediaInputPort;
class AudioNodeEngine;
class AudioNodeExternalInputStream;
class AudioNodeStream;
class CameraPreviewMediaStream;
/**
+ * Helper struct for binding a track listener to a specific TrackID.
+ */
+template<typename Listener>
+struct TrackBound
+{
+ RefPtr<Listener> mListener;
+ TrackID mTrackID;
+};
+
+/**
* A stream of synchronized audio and video data. All (not blocked) streams
* progress at the same rate --- "real time". Streams cannot seek. The only
* operation readers can perform on a stream is to read the next data.
*
* Consumers of a stream can be reading from it at different offsets, but that
* should only happen due to the order in which consumers are being run.
* Those offsets must not diverge in the long term, otherwise we would require
* unbounded buffering.
@@ -395,16 +440,20 @@ public:
// Suspend message reaches the graph, the stream stops processing. It
// ignores its inputs and produces silence/no video until Resumed. Its
// current time does not advance.
virtual void Suspend();
virtual void Resume();
// Events will be dispatched by calling methods of aListener.
virtual void AddListener(MediaStreamListener* aListener);
virtual void RemoveListener(MediaStreamListener* aListener);
+ virtual void AddTrackListener(MediaStreamTrackListener* aListener,
+ TrackID aTrackID);
+ virtual void RemoveTrackListener(MediaStreamTrackListener* aListener,
+ TrackID aTrackID);
// A disabled track has video replaced by black, and audio replaced by
// silence.
void SetTrackEnabled(TrackID aTrackID, bool aEnabled);
// Finish event will be notified by calling methods of aListener. It is the
// responsibility of the caller to remove aListener before it is destroyed.
void AddMainThreadListener(MainThreadMediaStreamListener* aListener);
// It's safe to call this even if aListener is not currently a listener;
@@ -491,16 +540,20 @@ public:
return !mAudioOutputs.IsEmpty();
}
void RemoveAudioOutputImpl(void* aKey);
void AddVideoOutputImpl(already_AddRefed<VideoFrameContainer> aContainer);
void RemoveVideoOutputImpl(VideoFrameContainer* aContainer);
void AddListenerImpl(already_AddRefed<MediaStreamListener> aListener);
void RemoveListenerImpl(MediaStreamListener* aListener);
void RemoveAllListenersImpl();
+ virtual void AddTrackListenerImpl(already_AddRefed<MediaStreamTrackListener> aListener,
+ TrackID aTrackID);
+ virtual void RemoveTrackListenerImpl(MediaStreamTrackListener* aListener,
+ TrackID aTrackID);
virtual void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled);
void AddConsumer(MediaInputPort* aPort)
{
mConsumers.AppendElement(aPort);
}
void RemoveConsumer(MediaInputPort* aPort)
{
@@ -644,16 +697,17 @@ protected:
float mVolume;
};
nsTArray<AudioOutput> mAudioOutputs;
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;
// GraphTime at which this stream starts blocking.
// This is only valid up to mStateComputedTime. The stream is considered to
// have not been blocked before mCurrentTime (its mBufferStartTime is increased
// as necessary to account for that time instead).
GraphTime mStartBlocking;
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.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 "MediaStreamTrack.h"
#include "DOMMediaStream.h"
+#include "MediaStreamGraph.h"
#include "nsIUUIDGenerator.h"
#include "nsServiceManagerUtils.h"
#ifdef LOG
#undef LOG
#endif
static PRLogModuleInfo* gMediaStreamTrackLog;
@@ -192,10 +193,28 @@ MediaStreamTrack::GetInputStream()
}
ProcessedMediaStream*
MediaStreamTrack::GetOwnedStream()
{
return GetStream()->GetOwnedStream();
}
+void
+MediaStreamTrack::AddListener(MediaStreamTrackListener* aListener)
+{
+ LOG(LogLevel::Debug, ("MediaStreamTrack %p adding listener %p",
+ this, aListener));
+
+ GetOwnedStream()->AddTrackListener(aListener, mTrackID);
+}
+
+void
+MediaStreamTrack::RemoveListener(MediaStreamTrackListener* aListener)
+{
+ LOG(LogLevel::Debug, ("MediaStreamTrack %p removing listener %p",
+ this, aListener));
+
+ GetOwnedStream()->RemoveTrackListener(aListener, mTrackID);
+}
+
} // namespace dom
} // namespace mozilla
--- a/dom/media/MediaStreamTrack.h
+++ b/dom/media/MediaStreamTrack.h
@@ -14,16 +14,17 @@
#include "PrincipalChangeObserver.h"
namespace mozilla {
class DOMMediaStream;
class MediaEnginePhotoCallback;
class MediaStream;
class MediaStreamGraph;
+class MediaStreamTrackListener;
class ProcessedMediaStream;
namespace dom {
class AudioStreamTrack;
class VideoStreamTrack;
/**
@@ -249,16 +250,28 @@ public:
/**
* Remove an added PrincipalChangeObserver from this track.
*
* Returns true if it was successfully removed.
*/
bool RemovePrincipalChangeObserver(PrincipalChangeObserver<MediaStreamTrack>* aObserver);
+ /**
+ * Adds a MediaStreamTrackListener to the MediaStreamGraph representation of
+ * this track.
+ */
+ void AddListener(MediaStreamTrackListener* aListener);
+
+ /**
+ * Removes a MediaStreamTrackListener from the MediaStreamGraph representation
+ * of this track.
+ */
+ void RemoveListener(MediaStreamTrackListener* aListener);
+
protected:
virtual ~MediaStreamTrack();
// Returns the original DOMMediaStream's underlying input stream.
MediaStream* GetInputStream();
// Returns the owning DOMMediaStream's underlying owned stream.
ProcessedMediaStream* GetOwnedStream();
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -234,16 +234,21 @@ TrackUnionStream::TrackUnionStream(DOMMe
nsAutoPtr<MediaSegment> segment;
segment = outputTrack->GetSegment()->CreateEmptyClone();
l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(), offset,
MediaStreamListener::TRACK_EVENT_ENDED,
*segment,
mTrackMap[aIndex].mInputPort->GetSource(),
mTrackMap[aIndex].mInputTrackID);
}
+ for (TrackBound<MediaStreamTrackListener>& b : mTrackListeners) {
+ if (b.mTrackID == outputTrack->GetID()) {
+ b.mListener->NotifyEnded();
+ }
+ }
outputTrack->SetEnded();
}
void TrackUnionStream::CopyTrackData(StreamBuffer::Track* aInputTrack,
uint32_t aMapIndex, GraphTime aFrom, GraphTime aTo,
bool* aOutputTrackFinished)
{
TrackMapEntry* map = &mTrackMap[aMapIndex];
@@ -295,12 +300,18 @@ TrackUnionStream::TrackUnionStream(DOMMe
}
}
ApplyTrackDisabling(outputTrack->GetID(), segment);
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
MediaStreamListener* l = mListeners[j];
l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(),
outputStart, 0, *segment);
}
+ for (TrackBound<MediaStreamTrackListener>& b : mTrackListeners) {
+ if (b.mTrackID != outputTrack->GetID()) {
+ continue;
+ }
+ b.mListener->NotifyQueuedChanges(Graph(), outputStart, *segment);
+ }
outputTrack->GetSegment()->AppendFrom(segment);
}
}
} // namespace mozilla