diff --git a/content/media/nsBuiltinDecoder.h b/content/media/nsBuiltinDecoder.h index 4bad12dee1ee..fad0c1b69c6e 100644 --- a/content/media/nsBuiltinDecoder.h +++ b/content/media/nsBuiltinDecoder.h @@ -303,6 +303,8 @@ public: // with the decode monitor held. Called on the state machine thread and // the main thread. virtual void StartBuffering() = 0; + + virtual void NotifyDataExhausted() = 0; }; class nsBuiltinDecoder : public nsMediaDecoder @@ -367,6 +369,10 @@ class nsBuiltinDecoder : public nsMediaDecoder // from the resource. void NotifyBytesConsumed(PRInt64 aBytes); + void NotifyDataExhausted() { + mDecoderStateMachine->NotifyDataExhausted(); + } + // Called when the video file has completed downloading. // Call on the main thread only. void ResourceLoaded(); diff --git a/content/media/nsBuiltinDecoderStateMachine.cpp b/content/media/nsBuiltinDecoderStateMachine.cpp index 00c3bffc7284..ef53b4f5ab39 100644 --- a/content/media/nsBuiltinDecoderStateMachine.cpp +++ b/content/media/nsBuiltinDecoderStateMachine.cpp @@ -901,7 +901,23 @@ PRBool nsBuiltinDecoderStateMachine::IsDecodeCloseToDownload() double threshold = (bufferTarget > 0 && length != -1) ? (length / (bufferTarget)) : LIVE_BUFFER_MARGIN; return (downloadPos - decodePos) < threshold; -} +} + +void nsBuiltinDecoderStateMachine::NotifyDataExhausted() +{ + MonitorAutoEnter mon(mDecoder->GetMonitor()); + nsMediaStream* stream = mDecoder->GetCurrentStream(); + NS_ASSERTION(!stream->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition), + "We shouldn't be notified in this case!"); + if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING && + mState == DECODER_STATE_DECODING && + !stream->IsSuspended()) + { + // Our decode has caught up with the download. Let's buffer to make sure + // we can play a decent amount of video in the future. + StartBuffering(); + } +} nsresult nsBuiltinDecoderStateMachine::Run() { @@ -991,17 +1007,6 @@ nsresult nsBuiltinDecoderStateMachine::Run() if (mState != DECODER_STATE_DECODING) continue; - - if (IsDecodeCloseToDownload() && - mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING && - !stream->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) && - !stream->IsSuspended()) - { - // We're low on decoded data, and/or our decode has caught up with - // the download. Let's buffer to make sure we can play a decent - // amount of video in the future. - StartBuffering(); - } } break; @@ -1107,6 +1112,11 @@ nsresult nsBuiltinDecoderStateMachine::Run() case DECODER_STATE_BUFFERING: { + if (IsPlaying()) { + StopPlayback(AUDIO_PAUSE); + mDecoder->GetMonitor().NotifyAll(); + } + TimeStamp now = TimeStamp::Now(); if (mBufferingEndOffset == -1) { // This is the first time we've entered the buffering state. @@ -1479,10 +1489,6 @@ void nsBuiltinDecoderStateMachine::LoadMetadata() void nsBuiltinDecoderStateMachine::StartBuffering() { mDecoder->GetMonitor().AssertCurrentThreadIn(); - if (IsPlaying()) { - StopPlayback(AUDIO_PAUSE); - mDecoder->GetMonitor().NotifyAll(); - } // We need to tell the element that buffering has started. // We can't just directly send an asynchronous runnable that diff --git a/content/media/nsBuiltinDecoderStateMachine.h b/content/media/nsBuiltinDecoderStateMachine.h index 8c7dd04c4f1a..1950b27ab16a 100644 --- a/content/media/nsBuiltinDecoderStateMachine.h +++ b/content/media/nsBuiltinDecoderStateMachine.h @@ -250,6 +250,8 @@ public: return mEndTime; } + void NotifyDataExhausted(); + protected: // Returns PR_TRUE if the decode is withing an estimated one tenth of a diff --git a/content/media/nsMediaDecoder.h b/content/media/nsMediaDecoder.h index e86b2cf1f976..086ac53ece8c 100644 --- a/content/media/nsMediaDecoder.h +++ b/content/media/nsMediaDecoder.h @@ -286,6 +286,11 @@ public: // to buffer, given the current download and playback rates. PRBool CanPlayThrough(); + // Called by the nsMediaStream when a read on the stream by the decoder + // is about to block due to insuffient data. Decoders may want to pause + // playback and go into buffering mode when this is called. + virtual void NotifyDataExhausted() = 0; + protected: // Start timer to update download progress information. diff --git a/content/media/nsMediaStream.cpp b/content/media/nsMediaStream.cpp index f159a95f651c..34fe6c449817 100644 --- a/content/media/nsMediaStream.cpp +++ b/content/media/nsMediaStream.cpp @@ -571,6 +571,15 @@ nsresult nsMediaChannelStream::Read(char* aBuffer, { NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); + PRInt64 pos = Tell(); + PRInt64 endOfRead = pos + aCount; + if (endOfRead > mCacheStream.GetCachedDataEnd(pos) && + !IsDataCachedToEndOfStream(pos)) { + // Our read will almost certainly block waiting for more data to download. + // Notify the decoder, so it can move to buffering state if need be. + mDecoder->NotifyDataExhausted(); + } + return mCacheStream.Read(aBuffer, aCount, aBytes); } diff --git a/content/media/test/test_paused_after_ended.html b/content/media/test/test_paused_after_ended.html index 5d8491dc5ec2..bd5bfa66bdbe 100644 --- a/content/media/test/test_paused_after_ended.html +++ b/content/media/test/test_paused_after_ended.html @@ -21,8 +21,8 @@ function ended(evt) { function startTest(test, token) { var v = document.createElement('video'); - v.token = v; - manager.started(v); + v.token = token; + manager.started(v.token); v.src = test.name; v._name = test.name; v._finished = false; diff --git a/content/media/wave/nsWaveDecoder.h b/content/media/wave/nsWaveDecoder.h index 0f27f99f4fbd..d5f77b7ebb49 100644 --- a/content/media/wave/nsWaveDecoder.h +++ b/content/media/wave/nsWaveDecoder.h @@ -244,6 +244,7 @@ class nsWaveDecoder : public nsMediaDecoder virtual void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset) {} + void NotifyDataExhausted() {} private: // Notifies the element that seeking has started. void SeekingStarted();