Bug 628665 - Resurrect nsBuiltinDecoderStateMachine::HasLowDecodedData(). r=roc

This commit is contained in:
Chris Pearce 2011-03-24 11:28:57 +13:00
Родитель f2eab9ebf2
Коммит f7ab69abdd
6 изменённых файлов: 77 добавлений и 59 удалений

Просмотреть файл

@ -303,8 +303,6 @@ 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
@ -369,10 +367,6 @@ 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();

Просмотреть файл

@ -117,6 +117,17 @@ static const double NORMAL_BUFFER_MARGIN = 100.0;
// undecoded data, we'll stop playback and start buffering.
static const int LIVE_BUFFER_MARGIN = 100000;
// Amount of excess ms of data to add in to the "should we buffer" calculation.
static const PRUint32 EXHAUSTED_DATA_MARGIN_MS = 60;
static TimeDuration MsToDuration(PRInt64 aMs) {
return TimeDuration::FromMilliseconds(static_cast<double>(aMs));
}
static PRInt64 DurationToMs(TimeDuration aDuration) {
return static_cast<PRInt64>(aDuration.ToSeconds() * 1000);
}
class nsAudioMetadataEventRunner : public nsRunnable
{
private:
@ -941,19 +952,21 @@ PRBool nsBuiltinDecoderStateMachine::IsDecodeCloseToDownload()
return (downloadPos - decodePos) < threshold;
}
void nsBuiltinDecoderStateMachine::NotifyDataExhausted()
PRBool nsBuiltinDecoderStateMachine::HasLowDecodedData(PRInt64 aAudioMs) const
{
MonitorAutoEnter mon(mDecoder->GetMonitor());
nsMediaStream* stream = mDecoder->GetCurrentStream();
if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING &&
mState == DECODER_STATE_DECODING &&
!stream->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
!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();
}
mDecoder->GetMonitor().AssertCurrentThreadIn();
// We consider ourselves low on decoded data if we're low on audio,
// provided we've not decoded to the end of the audio stream, or
// if we're only playing video and we're low on video frames, provided
// we've not decoded to the end of the video stream.
return ((HasAudio() &&
!mReader->mAudioQueue.IsFinished() &&
AudioDecodedMs() < aAudioMs)
||
(!HasAudio() &&
HasVideo() &&
!mReader->mVideoQueue.IsFinished() &&
static_cast<PRUint32>(mReader->mVideoQueue.GetSize()) < LOW_VIDEO_FRAMES));
}
nsresult nsBuiltinDecoderStateMachine::Run()
@ -1101,7 +1114,7 @@ nsresult nsBuiltinDecoderStateMachine::Run()
"Seek target should lie inside the first audio block after seek");
PRInt64 startTime = (audio && audio->mTime < seekTime) ? audio->mTime : seekTime;
mAudioStartTime = startTime;
mPlayDuration = TimeDuration::FromMilliseconds(startTime - mStartTime);
mPlayDuration = MsToDuration(startTime - mStartTime);
if (HasVideo()) {
nsAutoPtr<VideoData> video(mReader->mVideoQueue.PeekFront());
if (video) {
@ -1179,7 +1192,7 @@ nsresult nsBuiltinDecoderStateMachine::Run()
PRBool isLiveStream = mDecoder->GetCurrentStream()->GetLength() == -1;
if ((isLiveStream || !mDecoder->CanPlayThrough()) &&
elapsed < TimeDuration::FromSeconds(BUFFERING_WAIT) &&
stream->GetCachedDataEnd(mDecoder->mDecoderPosition) < mBufferingEndOffset &&
HasLowDecodedData(1000) &&
!stream->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
!stream->IsSuspended())
{
@ -1306,11 +1319,6 @@ void nsBuiltinDecoderStateMachine::AdvanceFrame()
// When it's time to display a frame, decode the frame and display it.
if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING) {
if (!IsPlaying()) {
StartPlayback();
mDecoder->GetMonitor().NotifyAll();
}
if (HasAudio() && mAudioStartTime == -1 && !mAudioCompleted) {
// We've got audio (so we should sync off the audio clock), but we've not
// played a sample on the audio thread, so we can't get a time from the
@ -1325,24 +1333,28 @@ void nsBuiltinDecoderStateMachine::AdvanceFrame()
// the end of the audio, use the audio clock. However if we've finished
// audio, or don't have audio, use the system clock.
PRInt64 clock_time = -1;
PRInt64 audio_time = GetAudioClock();
if (HasAudio() && !mAudioCompleted && audio_time != -1) {
clock_time = audio_time;
// Resync against the audio clock, while we're trusting the
// audio clock. This ensures no "drift", particularly on Linux.
mPlayDuration = TimeDuration::FromMilliseconds(clock_time - mStartTime);
mPlayStartTime = TimeStamp::Now();
if (!IsPlaying()) {
clock_time = DurationToMs(mPlayDuration) + mStartTime;
} else {
// Sound is disabled on this system. Sync to the system clock.
TimeDuration t = TimeStamp::Now() - mPlayStartTime + mPlayDuration;
clock_time = (PRInt64)(1000 * t.ToSeconds());
// Ensure the clock can never go backwards.
NS_ASSERTION(mCurrentFrameTime <= clock_time, "Clock should go forwards");
clock_time = NS_MAX(mCurrentFrameTime, clock_time) + mStartTime;
PRInt64 audio_time = GetAudioClock();
if (HasAudio() && !mAudioCompleted && audio_time != -1) {
clock_time = audio_time;
// Resync against the audio clock, while we're trusting the
// audio clock. This ensures no "drift", particularly on Linux.
mPlayDuration = MsToDuration(clock_time - mStartTime);
mPlayStartTime = TimeStamp::Now();
} else {
// Sound is disabled on this system. Sync to the system clock.
clock_time = DurationToMs(TimeStamp::Now() - mPlayStartTime + mPlayDuration);
// Ensure the clock can never go backwards.
NS_ASSERTION(mCurrentFrameTime <= clock_time, "Clock should go forwards");
clock_time = NS_MAX(mCurrentFrameTime, clock_time) + mStartTime;
}
}
// Skip frames up to the frame at the playback position, and figure out
// the time remaining until it's time to display the next frame.
PRInt64 remainingTime = AUDIO_DURATION_MS;
NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time.");
nsAutoPtr<VideoData> currentFrame;
if (mReader->mVideoQueue.GetSize() > 0) {
@ -1359,13 +1371,38 @@ void nsBuiltinDecoderStateMachine::AdvanceFrame()
// Current frame has already been presented, wait until it's time to
// present the next frame.
if (frame && !currentFrame) {
PRInt64 now = (TimeStamp::Now() - mPlayStartTime + mPlayDuration).ToMilliseconds();
PRInt64 now = IsPlaying()
? DurationToMs(TimeStamp::Now() - mPlayStartTime + mPlayDuration)
: DurationToMs(mPlayDuration);
remainingTime = frame->mTime - mStartTime - now;
}
}
// Check to see if we don't have enough data to play up to the next frame.
// If we don't, switch to buffering mode.
nsMediaStream* stream = mDecoder->GetCurrentStream();
if (mState == DECODER_STATE_DECODING &&
mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING &&
HasLowDecodedData(remainingTime + EXHAUSTED_DATA_MARGIN_MS) &&
!stream->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
!stream->IsSuspended())
{
if (currentFrame) {
mReader->mVideoQueue.PushFront(currentFrame.forget());
}
mState = DECODER_STATE_BUFFERING;
return;
}
// We've got enough data to keep playing until at least the next frame.
// Start playing now if need be.
if (!IsPlaying()) {
StartPlayback();
mDecoder->GetMonitor().NotifyAll();
}
if (currentFrame) {
// Decode one frame and display it
// Decode one frame and display it.
TimeStamp presTime = mPlayStartTime - mPlayDuration +
TimeDuration::FromMilliseconds(currentFrame->mTime - mStartTime);
NS_ASSERTION(currentFrame->mTime >= mStartTime, "Should have positive frame time");
@ -1376,7 +1413,7 @@ void nsBuiltinDecoderStateMachine::AdvanceFrame()
RenderVideoFrame(currentFrame, presTime);
}
mDecoder->GetFrameStatistics().NotifyPresentedFrame();
PRInt64 now = (TimeStamp::Now() - mPlayStartTime + mPlayDuration).ToMilliseconds();
PRInt64 now = DurationToMs(TimeStamp::Now() - mPlayStartTime + mPlayDuration);
remainingTime = currentFrame->mEndTime - mStartTime - now;
currentFrame = nsnull;
}

Просмотреть файл

@ -250,10 +250,12 @@ public:
return mEndTime;
}
void NotifyDataExhausted();
protected:
// Returns PR_TRUE if we'v got less than aAudioMs ms of decoded and playable
// data. The decoder monitor must be held.
PRBool HasLowDecodedData(PRInt64 aAudioMs) const;
// Returns PR_TRUE if the decode is withing an estimated one tenth of a
// second's worth of data of the download, i.e. the decode has almost
// caught up with the download. If we can't estimate one tenth of a second's

Просмотреть файл

@ -382,11 +382,6 @@ 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.

Просмотреть файл

@ -588,15 +588,6 @@ 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);
}

Просмотреть файл

@ -244,7 +244,6 @@ 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();