diff --git a/dom/camera/CameraPreviewMediaStream.cpp b/dom/camera/CameraPreviewMediaStream.cpp index b00492a3e055..d5ea13795eb3 100644 --- a/dom/camera/CameraPreviewMediaStream.cpp +++ b/dom/camera/CameraPreviewMediaStream.cpp @@ -127,6 +127,7 @@ CameraPreviewMediaStream::OnPreviewStateChange(bool aActive) l->NotifyQueuedTrackChanges(mFakeMediaStreamGraph, TRACK_VIDEO, 0, MediaStreamListener::TRACK_EVENT_CREATED, tmpSegment); + l->NotifyFinishedTrackCreation(mFakeMediaStreamGraph); } } } diff --git a/dom/media/DOMMediaStream.cpp b/dom/media/DOMMediaStream.cpp index 9d78ed62bdef..b331a06f161c 100644 --- a/dom/media/DOMMediaStream.cpp +++ b/dom/media/DOMMediaStream.cpp @@ -98,6 +98,35 @@ public: } } + class TracksCreatedRunnable : public nsRunnable { + public: + explicit TracksCreatedRunnable(StreamListener* aListener) + : mListener(aListener) + { + } + + NS_IMETHOD Run() + { + MOZ_ASSERT(NS_IsMainThread()); + + DOMMediaStream* stream = mListener->GetStream(); + if (!stream) { + return NS_OK; + } + + stream->TracksCreated(); + return NS_OK; + } + + nsRefPtr mListener; + }; + + virtual void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) MOZ_OVERRIDE + { + nsRefPtr runnable = new TracksCreatedRunnable(this); + aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget()); + } + private: // These fields may only be accessed on the main thread DOMMediaStream* mStream; @@ -145,7 +174,7 @@ NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream) DOMMediaStream::DOMMediaStream() : mLogicalStreamStartTime(0), - mStream(nullptr), mHintContents(0), mTrackTypesAvailable(0), + mStream(nullptr), mTracksCreated(false), mNotifiedOfMediaStreamGraphShutdown(false), mCORSMode(CORS_NONE) { nsresult rv; @@ -475,10 +504,18 @@ DOMMediaStream::OnTracksAvailable(OnTracksAvailableCallback* aRunnable) CheckTracksAvailable(); } +void +DOMMediaStream::TracksCreated() +{ + MOZ_ASSERT(!mTracks.IsEmpty()); + mTracksCreated = true; + CheckTracksAvailable(); +} + void DOMMediaStream::CheckTracksAvailable() { - if (mTrackTypesAvailable == 0) { + if (!mTracksCreated) { return; } nsTArray > callbacks; diff --git a/dom/media/DOMMediaStream.h b/dom/media/DOMMediaStream.h index d48ea63e6a74..2c7aae9b2dae 100644 --- a/dom/media/DOMMediaStream.h +++ b/dom/media/DOMMediaStream.h @@ -279,6 +279,10 @@ protected: already_AddRefed CreateAudioTrack(AudioStreamTrack* aStreamTrack); already_AddRefed CreateVideoTrack(VideoStreamTrack* aStreamTrack); + // Called when MediaStreamGraph has finished an iteration where tracks were + // created. + void TracksCreated(); + void CheckTracksAvailable(); class StreamListener; @@ -299,6 +303,9 @@ protected: nsTArray > mRunOnTracksAvailable; + // Set to true after MediaStreamGraph has created tracks for mStream. + bool mTracksCreated; + nsString mID; // Keep these alive until the stream finishes diff --git a/dom/media/MediaStreamGraph.cpp b/dom/media/MediaStreamGraph.cpp index 94945177324b..c19e8fcaf404 100644 --- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -190,11 +190,11 @@ MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream, } } 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); - for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) { - MediaStreamListener* l = aStream->mListeners[j]; + 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, @@ -212,6 +212,7 @@ MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream, // data->mData with an empty clone. data->mData = segment->CreateEmptyClone(); data->mCommands &= ~SourceMediaStream::TRACK_CREATE; + notifiedTrackCreated = true; } else if (data->mData->GetDuration() > 0) { MediaSegment* dest = aStream->mBuffer.FindTrack(data->mID)->GetSegment(); STREAM_LOG(PR_LOG_DEBUG+1, ("SourceMediaStream %p track %d, advancing end from %lld to %lld", @@ -226,6 +227,11 @@ MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream, aStream->mUpdateTracks.RemoveElementAt(i); } } + if (notifiedTrackCreated) { + for (MediaStreamListener* l : aStream->mListeners) { + l->NotifyFinishedTrackCreation(this); + } + } if (!aStream->mFinished) { aStream->mBuffer.AdvanceKnownTracksTime(aStream->mUpdateKnownTracksTime); } @@ -1938,6 +1944,9 @@ MediaStream::EnsureTrack(TrackID aTrackId) l->NotifyQueuedTrackChanges(Graph(), aTrackId, 0, MediaStreamListener::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 = &mBuffer.AddTrack(aTrackId, 0, segment.forget()); } diff --git a/dom/media/MediaStreamGraph.h b/dom/media/MediaStreamGraph.h index 09f38808a082..522537e36a2f 100644 --- a/dom/media/MediaStreamGraph.h +++ b/dom/media/MediaStreamGraph.h @@ -177,6 +177,13 @@ public: StreamTime aTrackOffset, uint32_t aTrackEvents, const MediaSegment& aQueuedMedia) {} + + /** + * 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) {} }; /** diff --git a/dom/media/TrackUnionStream.cpp b/dom/media/TrackUnionStream.cpp index 5bb673fa2ca0..c7d545ec539b 100644 --- a/dom/media/TrackUnionStream.cpp +++ b/dom/media/TrackUnionStream.cpp @@ -94,6 +94,7 @@ TrackUnionStream::TrackUnionStream(DOMMediaStream* aWrapper) : if (!stream->HasCurrentData()) { allHaveCurrentData = false; } + bool trackAdded = false; for (StreamBuffer::TrackIter tracks(stream->GetStreamBuffer()); !tracks.IsEnded(); tracks.Next()) { bool found = false; @@ -115,12 +116,18 @@ TrackUnionStream::TrackUnionStream(DOMMediaStream* aWrapper) : } if (!found && (!mFilterCallback || mFilterCallback(tracks.get()))) { bool trackFinished = false; + trackAdded = true; uint32_t mapIndex = AddTrack(mInputs[i], tracks.get(), aFrom); CopyTrackData(tracks.get(), mapIndex, aFrom, aTo, &trackFinished); mappedTracksFinished.AppendElement(trackFinished); mappedTracksWithMatchingInputTracks.AppendElement(true); } } + if (trackAdded) { + for (MediaStreamListener* l : mListeners) { + l->NotifyFinishedTrackCreation(Graph()); + } + } } for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) { if (mappedTracksFinished[i]) {