author | Randell Jesup <rjesup@jesup.org> |
Wed, 29 Oct 2014 10:47:28 -0400 | |
changeset 213040 | 8ccb2bf14892d13c278580667b2a1670e5ec2b88 |
parent 213039 | e82273c162bf9e7fc764d426a217994c31b702b9 |
child 213041 | 5e5b3c89df165dae1ee53e682641ccd2599e1200 |
push id | 27738 |
push user | cbook@mozilla.com |
push date | Thu, 30 Oct 2014 13:46:07 +0000 |
treeherder | mozilla-central@1aa1b23d799e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | padenot |
bugs | 1085356 |
milestone | 36.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
|
--- a/dom/media/GraphDriver.cpp +++ b/dom/media/GraphDriver.cpp @@ -294,16 +294,18 @@ ThreadedDriver::RunThread() mNextStateComputedTime = mGraphImpl->RoundUpToNextAudioBlock( nextCurrentTime + mGraphImpl->MillisecondsToMediaTime(AUDIO_TARGET_MS)); STREAM_LOG(PR_LOG_DEBUG, ("interval[%ld; %ld] state[%ld; %ld]", (long)mIterationStart, (long)mIterationEnd, (long)mStateComputedTime, (long)mNextStateComputedTime)); + mGraphImpl->mFlushSourcesNow = mGraphImpl->mFlushSourcesOnNextIteration; + mGraphImpl->mFlushSourcesOnNextIteration = false; stillProcessing = mGraphImpl->OneIteration(prevCurrentTime, nextCurrentTime, StateComputedTime(), mNextStateComputedTime); if (mNextDriver && stillProcessing) { STREAM_LOG(PR_LOG_DEBUG, ("Switching to AudioCallbackDriver")); mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd, @@ -791,17 +793,19 @@ bool AudioCallbackDriver::OSXDeviceSwitchingWorkaround() { MonitorAutoLock mon(GraphImpl()->GetMonitor()); if (mSelfReference) { // Apparently, depending on the osx version, on device switch, the // callback is called "some" number of times, and then stops being called, // and then gets called again. 10 is to be safe, it's a low-enough number // of milliseconds anyways (< 100ms) + //STREAM_LOG(PR_LOG_DEBUG, ("Callbacks during switch: %d", mCallbackReceivedWhileSwitching+1)); if (mCallbackReceivedWhileSwitching++ >= 10) { + STREAM_LOG(PR_LOG_DEBUG, ("Got %d callbacks, switching back to CallbackDriver", mCallbackReceivedWhileSwitching)); // If we have a self reference, we have fallen back temporarily on a // system clock driver, but we just got called back, that means the osx // audio backend has switched to the new device. // Ask the graph to switch back to the previous AudioCallbackDriver // (`this`), and when the graph has effectively switched, we can drop // the self reference and unref the SystemClockDriver we fallen back on. if (GraphImpl()->CurrentDriver() == this) { mSelfReference.Drop(this); @@ -1027,18 +1031,20 @@ AudioCallbackDriver::DeviceChangedCallba // SourceMediaStream. if (!GraphImpl()->Running()) { return; } if (mSelfReference) { return; } + STREAM_LOG(PR_LOG_ERROR, ("Switching to SystemClockDriver during output switch")); mSelfReference.Take(this); mCallbackReceivedWhileSwitching = 0; + mGraphImpl->mFlushSourcesOnNextIteration = true; mNextDriver = new SystemClockDriver(GraphImpl()); mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd, mStateComputedTime, mNextStateComputedTime); mGraphImpl->SetCurrentDriver(mNextDriver); mNextDriver->Start(); #endif }
--- a/dom/media/MediaSegment.h +++ b/dom/media/MediaSegment.h @@ -84,16 +84,20 @@ public: */ virtual void AppendSlice(const MediaSegment& aSource, TrackTicks aStart, TrackTicks aEnd) = 0; /** * Replace all contents up to aDuration with null data. */ virtual void ForgetUpTo(TrackTicks aDuration) = 0; /** + * Forget all data buffered after a given point + */ + virtual void FlushAfter(TrackTicks aNewEnd) = 0; + /** * Insert aDuration of null data at the start of the segment. */ virtual void InsertNullDataAtStart(TrackTicks aDuration) = 0; /** * Insert aDuration of null data at the end of the segment. */ virtual void AppendNullData(TrackTicks aDuration) = 0; /** @@ -171,16 +175,39 @@ public: mDuration += extraToForget; } return; } RemoveLeading(aDuration, 0); mChunks.InsertElementAt(0)->SetNull(aDuration); mDuration += aDuration; } + virtual void FlushAfter(TrackTicks aNewEnd) + { + if (mChunks.IsEmpty()) { + return; + } + + if (mChunks[0].IsNull()) { + TrackTicks extraToKeep = aNewEnd - mChunks[0].GetDuration(); + if (extraToKeep < 0) { + // reduce the size of the Null, get rid of everthing else + mChunks[0].SetNull(aNewEnd); + extraToKeep = 0; + } + RemoveTrailing(extraToKeep, 1); + } else { + if (aNewEnd > mDuration) { + NS_ASSERTION(aNewEnd <= mDuration, "can't add data in FlushAfter"); + return; + } + RemoveTrailing(aNewEnd, 0); + } + mDuration = aNewEnd; + } virtual void InsertNullDataAtStart(TrackTicks aDuration) { if (aDuration <= 0) { return; } if (!mChunks.IsEmpty() && mChunks[0].IsNull()) { mChunks[0].mDuration += aDuration; } else { @@ -345,16 +372,38 @@ protected: } t -= c->GetDuration(); chunksToRemove = i + 1 - aStartIndex; } mChunks.RemoveElementsAt(aStartIndex, chunksToRemove); mDuration -= aDuration - t; } + void RemoveTrailing(TrackTicks aKeep, uint32_t aStartIndex) + { + NS_ASSERTION(aKeep >= 0, "Can't keep negative duration"); + TrackTicks t = aKeep; + uint32_t i; + for (i = aStartIndex; i < mChunks.Length(); ++i) { + Chunk* c = &mChunks[i]; + if (c->GetDuration() > t) { + c->SliceTo(0, t); + break; + } + t -= c->GetDuration(); + if (t == 0) { + break; + } + } + if (i+1 < mChunks.Length()) { + mChunks.RemoveElementsAt(i+1, mChunks.Length() - (i+1)); + } + // Caller must adjust mDuration + } + nsTArray<Chunk> mChunks; #ifdef MOZILLA_INTERNAL_API mozilla::TimeStamp mTimeStamp; #endif }; }
--- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -1434,16 +1434,17 @@ MediaStreamGraphImpl::OneIteration(Graph // stream is responsible for calling Destroy on them. return false; } CurrentDriver()->WaitForNextIteration(); SwapMessageQueues(); } + mFlushSourcesNow = false; return true; } void MediaStreamGraphImpl::ApplyStreamUpdate(StreamUpdate* aUpdate) { mMonitor.AssertCurrentThreadOwns(); @@ -2717,16 +2718,18 @@ MediaStreamGraphImpl::MediaStreamGraphIm , mNeedAnotherIteration(false) , mGraphDriverAsleep(false) , mMonitor("MediaStreamGraphImpl") , mLifecycleState(LIFECYCLE_THREAD_NOT_STARTED) , mEndTime(GRAPH_TIME_MAX) , mSampleRate(aSampleRate) , mForceShutDown(false) , mPostedRunInStableStateEvent(false) + , mFlushSourcesNow(false) + , mFlushSourcesOnNextIteration(false) , mDetectedNotRunning(false) , mPostedRunInStableState(false) , mRealtime(aRealtime) , mNonRealtimeProcessing(false) , mStreamOrderDirty(false) , mLatencyLog(AsyncLatencyLogger::Get()) #ifdef MOZ_WEBRTC , mFarendObserverRef(nullptr)
--- a/dom/media/MediaStreamGraphImpl.h +++ b/dom/media/MediaStreamGraphImpl.h @@ -605,16 +605,23 @@ public: */ bool mForceShutDown; /** * True when we have posted an event to the main thread to run * RunInStableState() and the event hasn't run yet. */ bool mPostedRunInStableStateEvent; + /** + * Used to flush any accumulated data when the output streams + * may have stalled (on Mac after an output device change) + */ + bool mFlushSourcesNow; + bool mFlushSourcesOnNextIteration; + // Main thread only /** * Messages posted by the current event loop task. These are forwarded to * the media graph thread during RunInStableState. We can't forward them * immediately because we want all messages between stable states to be * processed as an atomic batch. */
--- a/dom/media/StreamBuffer.h +++ b/dom/media/StreamBuffer.h @@ -149,16 +149,22 @@ public: MediaSegment* RemoveSegment() { return mSegment.forget(); } void ForgetUpTo(TrackTicks aTime) { mSegment->ForgetUpTo(aTime); } + void FlushAfter(TrackTicks aNewEnd) + { + // Forget everything after a given endpoint + // a specified amount + mSegment->FlushAfter(aNewEnd); + } size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { size_t amount = aMallocSizeOf(this); if (mSegment) { amount += mSegment->SizeOfIncludingThis(aMallocSizeOf); } return amount;
--- a/dom/media/TrackUnionStream.cpp +++ b/dom/media/TrackUnionStream.cpp @@ -258,18 +258,19 @@ TrackUnionStream::TrackUnionStream(DOMMe StreamTime inputEnd = source->GraphTimeToStreamTime(interval.mEnd); TrackTicks inputTrackEndPoint = aInputTrack->GetEnd(); if (aInputTrack->IsEnded() && aInputTrack->GetEndTimeRoundDown() <= inputEnd) { *aOutputTrackFinished = true; } - if (interval.mStart >= interval.mEnd) + if (interval.mStart >= interval.mEnd) { break; + } next = interval.mEnd; // Ticks >= startTicks and < endTicks are in the interval StreamTime outputEnd = GraphTimeToStreamTime(interval.mEnd); TrackTicks startTicks = outputTrack->GetEnd(); StreamTime outputStart = GraphTimeToStreamTime(interval.mStart); MOZ_ASSERT(startTicks == TimeToTicksRoundUp(rate, outputStart), "Samples missing"); TrackTicks endTicks = TimeToTicksRoundUp(rate, outputEnd); @@ -300,16 +301,24 @@ TrackUnionStream::TrackUnionStream(DOMMe // Start of a new series of intervals where neither stream is blocked. map->mEndOfConsumedInputTicks = TimeToTicksRoundDown(rate, inputStart) - 1; } TrackTicks inputStartTicks = map->mEndOfConsumedInputTicks; TrackTicks inputEndTicks = inputStartTicks + ticks; map->mEndOfConsumedInputTicks = inputEndTicks; map->mEndOfLastInputIntervalInInputStream = inputEnd; map->mEndOfLastInputIntervalInOutputStream = outputEnd; + + if (GraphImpl()->mFlushSourcesNow) { + TrackTicks flushto = inputEndTicks; + STREAM_LOG(PR_LOG_DEBUG, ("TrackUnionStream %p flushing after %lld of %lld ticks of input data from track %d for track %d", + this, flushto, aInputTrack->GetSegment()->GetDuration(), aInputTrack->GetID(), outputTrack->GetID())); + aInputTrack->FlushAfter(flushto); + MOZ_ASSERT(inputTrackEndPoint >= aInputTrack->GetEnd()); + } // Now we prove that the above properties hold: // Property #1: trivial by construction. // Property #3: trivial by construction. Between every two // intervals where both streams are not blocked, the above if condition // is false and mEndOfConsumedInputTicks advances exactly to match // the ticks that were consumed. // Property #2: // Let originalOutputStart be the value of outputStart and originalInputStart