зеркало из https://github.com/mozilla/gecko-dev.git
Bug 543769 - Buffer non-autobuffer videos upon first playback to ensure smooth playback. r=roc a=blocking2.0
This commit is contained in:
Родитель
3566c16b72
Коммит
5f69fff203
|
@ -1010,7 +1010,7 @@ void nsHTMLMediaElement::StopSuspendingAfterFirstFrame()
|
|||
return;
|
||||
mSuspendedAfterFirstFrame = PR_FALSE;
|
||||
if (mDecoder) {
|
||||
mDecoder->Resume();
|
||||
mDecoder->Resume(PR_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1662,11 +1662,6 @@ PRBool nsHTMLMediaElement::ShouldCheckAllowOrigin()
|
|||
PR_TRUE);
|
||||
}
|
||||
|
||||
// Number of bytes to add to the download size when we're computing
|
||||
// when the download will finish --- a safety margin in case bandwidth
|
||||
// or other conditions are worse than expected
|
||||
static const PRInt32 gDownloadSizeSafetyMargin = 1000000;
|
||||
|
||||
void nsHTMLMediaElement::UpdateReadyStateForData(NextFrameStatus aNextFrame)
|
||||
{
|
||||
if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
|
||||
|
@ -1677,8 +1672,6 @@ void nsHTMLMediaElement::UpdateReadyStateForData(NextFrameStatus aNextFrame)
|
|||
return;
|
||||
}
|
||||
|
||||
nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
|
||||
|
||||
if (aNextFrame != NEXT_FRAME_AVAILABLE) {
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
|
||||
if (!mWaitingFired && aNextFrame == NEXT_FRAME_UNAVAILABLE_BUFFERING) {
|
||||
|
@ -1693,25 +1686,17 @@ void nsHTMLMediaElement::UpdateReadyStateForData(NextFrameStatus aNextFrame)
|
|||
// make a real estimate, so we go straight to HAVE_ENOUGH_DATA once
|
||||
// we've downloaded enough data that our download rate is considered
|
||||
// reliable. We have to move to HAVE_ENOUGH_DATA at some point or
|
||||
// autoplay elements for live streams will never play.
|
||||
// autoplay elements for live streams will never play. Otherwise we
|
||||
// move to HAVE_ENOUGH_DATA if we can play through the entire media
|
||||
// without stopping to buffer.
|
||||
nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
|
||||
if (stats.mTotalBytes < 0 ? stats.mDownloadRateReliable :
|
||||
stats.mTotalBytes == stats.mDownloadPosition) {
|
||||
stats.mTotalBytes == stats.mDownloadPosition ||
|
||||
mDecoder->CanPlayThrough())
|
||||
{
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stats.mDownloadRateReliable && stats.mPlaybackRateReliable) {
|
||||
PRInt64 bytesToDownload = stats.mTotalBytes - stats.mDownloadPosition;
|
||||
PRInt64 bytesToPlayback = stats.mTotalBytes - stats.mPlaybackPosition;
|
||||
double timeToDownload =
|
||||
(bytesToDownload + gDownloadSizeSafetyMargin)/stats.mDownloadRate;
|
||||
double timeToPlay = bytesToPlayback/stats.mPlaybackRate;
|
||||
if (timeToDownload <= timeToPlay) {
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA);
|
||||
}
|
||||
|
||||
|
@ -1951,7 +1936,7 @@ void nsHTMLMediaElement::NotifyOwnerDocumentActivityChanged()
|
|||
mDecoder->Pause();
|
||||
mDecoder->Suspend();
|
||||
} else {
|
||||
mDecoder->Resume();
|
||||
mDecoder->Resume(PR_FALSE);
|
||||
if (!mPaused && !mDecoder->IsEnded()) {
|
||||
mDecoder->Play();
|
||||
}
|
||||
|
|
|
@ -446,7 +446,8 @@ NS_IMETHODIMP nsBuiltinDecoder::Observe(nsISupports *aSubjet,
|
|||
nsMediaDecoder::Statistics
|
||||
nsBuiltinDecoder::GetStatistics()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ASSERTION(NS_IsMainThread() || OnStateMachineThread(),
|
||||
"Should be on main or state machine thread.");
|
||||
Statistics result;
|
||||
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
|
@ -788,12 +789,16 @@ void nsBuiltinDecoder::Suspend()
|
|||
}
|
||||
}
|
||||
|
||||
void nsBuiltinDecoder::Resume()
|
||||
void nsBuiltinDecoder::Resume(PRBool aForceBuffering)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mStream) {
|
||||
mStream->Resume();
|
||||
}
|
||||
if (aForceBuffering) {
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
mDecoderStateMachine->StartBuffering();
|
||||
}
|
||||
}
|
||||
|
||||
void nsBuiltinDecoder::StopProgressUpdates()
|
||||
|
|
|
@ -293,6 +293,12 @@ public:
|
|||
// Only called on the decoder thread. Must be called with
|
||||
// the decode monitor held.
|
||||
virtual void UpdatePlaybackPosition(PRInt64 aTime) = 0;
|
||||
|
||||
// Causes the state machine to switch to buffering state, and to
|
||||
// immediately stop playback and buffer downloaded data. Must be called
|
||||
// with the decode monitor held. Called on the state machine thread and
|
||||
// the main thread.
|
||||
virtual void StartBuffering() = 0;
|
||||
};
|
||||
|
||||
class nsBuiltinDecoder : public nsMediaDecoder
|
||||
|
@ -393,7 +399,7 @@ class nsBuiltinDecoder : public nsMediaDecoder
|
|||
// Resume any media downloads that have been suspended. Called by the
|
||||
// media element when it is restored from the bfcache. Call on the
|
||||
// main thread only.
|
||||
virtual void Resume();
|
||||
virtual void Resume(PRBool aForceBuffering);
|
||||
|
||||
// Tells our nsMediaStream to put all loads in the background.
|
||||
virtual void MoveLoadsToBackground();
|
||||
|
|
|
@ -817,29 +817,7 @@ nsresult nsBuiltinDecoderStateMachine::Run()
|
|||
// There is at most one frame in the queue and there's
|
||||
// more data to load. Let's buffer to make sure we can play a
|
||||
// decent amount of video in the future.
|
||||
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
|
||||
// eventually fires the "waiting" event. The problem is that
|
||||
// there might be pending main-thread events, such as "data
|
||||
// received" notifications, that mean we're not actually still
|
||||
// buffering by the time this runnable executes. So instead
|
||||
// we just trigger UpdateReadyStateForData; when it runs, it
|
||||
// will check the current state and decide whether to tell
|
||||
// the element we're buffering or not.
|
||||
UpdateReadyState();
|
||||
|
||||
mBufferingStart = TimeStamp::Now();
|
||||
PRPackedBool reliable;
|
||||
double playbackRate = mDecoder->ComputePlaybackRate(&reliable);
|
||||
mBufferingEndOffset = mDecoder->mDecoderPosition +
|
||||
BUFFERING_RATE(playbackRate) * BUFFERING_WAIT;
|
||||
mState = DECODER_STATE_BUFFERING;
|
||||
LOG(PR_LOG_DEBUG, ("Changed state from DECODING to BUFFERING"));
|
||||
StartBuffering();
|
||||
} else {
|
||||
if (mBufferExhausted) {
|
||||
// This will wake up the decode thread and force it to try to
|
||||
|
@ -943,10 +921,12 @@ nsresult nsBuiltinDecoderStateMachine::Run()
|
|||
case DECODER_STATE_BUFFERING:
|
||||
{
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
if (now - mBufferingStart < TimeDuration::FromSeconds(BUFFERING_WAIT) &&
|
||||
mDecoder->GetCurrentStream()->GetCachedDataEnd(mDecoder->mDecoderPosition) < mBufferingEndOffset &&
|
||||
!mDecoder->GetCurrentStream()->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
|
||||
!mDecoder->GetCurrentStream()->IsSuspendedByCache()) {
|
||||
nsMediaStream* stream = mDecoder->GetCurrentStream();
|
||||
if (!mDecoder->CanPlayThrough() &&
|
||||
now - mBufferingStart < TimeDuration::FromSeconds(BUFFERING_WAIT) &&
|
||||
stream->GetCachedDataEnd(mDecoder->mDecoderPosition) < mBufferingEndOffset &&
|
||||
!stream->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
|
||||
!stream->IsSuspendedByCache()) {
|
||||
LOG(PR_LOG_DEBUG,
|
||||
("In buffering: buffering data until %d bytes available or %f seconds",
|
||||
PRUint32(mBufferingEndOffset - mDecoder->GetCurrentStream()->GetCachedDataEnd(mDecoder->mDecoderPosition)),
|
||||
|
@ -1273,3 +1253,32 @@ void nsBuiltinDecoderStateMachine::LoadMetadata()
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void nsBuiltinDecoderStateMachine::StartBuffering()
|
||||
{
|
||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||
mBufferExhausted = PR_TRUE;
|
||||
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
|
||||
// eventually fires the "waiting" event. The problem is that
|
||||
// there might be pending main-thread events, such as "data
|
||||
// received" notifications, that mean we're not actually still
|
||||
// buffering by the time this runnable executes. So instead
|
||||
// we just trigger UpdateReadyStateForData; when it runs, it
|
||||
// will check the current state and decide whether to tell
|
||||
// the element we're buffering or not.
|
||||
UpdateReadyState();
|
||||
|
||||
mBufferingStart = TimeStamp::Now();
|
||||
PRPackedBool reliable;
|
||||
double playbackRate = mDecoder->ComputePlaybackRate(&reliable);
|
||||
mBufferingEndOffset = mDecoder->mDecoderPosition +
|
||||
BUFFERING_RATE(playbackRate) * BUFFERING_WAIT;
|
||||
mState = DECODER_STATE_BUFFERING;
|
||||
LOG(PR_LOG_DEBUG, ("Changed state from DECODING to BUFFERING"));
|
||||
}
|
||||
|
|
|
@ -168,6 +168,7 @@ public:
|
|||
virtual void ClearPositionChangeFlag();
|
||||
virtual void SetSeekable(PRBool aSeekable);
|
||||
virtual void UpdatePlaybackPosition(PRInt64 aTime);
|
||||
virtual void StartBuffering();
|
||||
|
||||
|
||||
// Load metadata Called on the state machine thread. The decoder monitor must be held with
|
||||
|
|
|
@ -231,3 +231,22 @@ void nsMediaDecoder::SetVideoData(const gfxIntSize& aSize,
|
|||
mImageContainer->SetCurrentImage(aImage);
|
||||
}
|
||||
}
|
||||
|
||||
// Number of bytes to add to the download size when we're computing
|
||||
// when the download will finish --- a safety margin in case bandwidth
|
||||
// or other conditions are worse than expected
|
||||
static const PRInt32 gDownloadSizeSafetyMargin = 1000000;
|
||||
|
||||
PRBool nsMediaDecoder::CanPlayThrough()
|
||||
{
|
||||
Statistics stats = GetStatistics();
|
||||
if (!stats.mDownloadRateReliable || !stats.mPlaybackRateReliable) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
PRInt64 bytesToDownload = stats.mTotalBytes - stats.mDownloadPosition;
|
||||
PRInt64 bytesToPlayback = stats.mTotalBytes - stats.mPlaybackPosition;
|
||||
double timeToDownload =
|
||||
(bytesToDownload + gDownloadSizeSafetyMargin)/stats.mDownloadRate;
|
||||
double timeToPlay = bytesToPlayback/stats.mPlaybackRate;
|
||||
return timeToDownload <= timeToPlay;
|
||||
}
|
||||
|
|
|
@ -209,8 +209,10 @@ public:
|
|||
// media element when it is restored from the bfcache, or when we need
|
||||
// to stop throttling the download. Call on the main thread only.
|
||||
// The download will only actually resume once as many Resume calls
|
||||
// have been made as Suspend calls.
|
||||
virtual void Resume() = 0;
|
||||
// have been made as Suspend calls. When aForceBuffering is PR_TRUE,
|
||||
// we force the decoder to go into buffering state before resuming
|
||||
// playback.
|
||||
virtual void Resume(PRBool aForceBuffering) = 0;
|
||||
|
||||
// Returns a weak reference to the media element we're decoding for,
|
||||
// if it's available.
|
||||
|
@ -234,6 +236,10 @@ public:
|
|||
float aPixelAspectRatio,
|
||||
Image* aImage);
|
||||
|
||||
// Returns PR_TRUE if we can play the entire media through without stopping
|
||||
// to buffer, given the current download and playback rates.
|
||||
PRBool CanPlayThrough();
|
||||
|
||||
protected:
|
||||
|
||||
// Start timer to update download progress information.
|
||||
|
|
|
@ -1639,7 +1639,7 @@ nsWaveDecoder::Suspend()
|
|||
}
|
||||
|
||||
void
|
||||
nsWaveDecoder::Resume()
|
||||
nsWaveDecoder::Resume(PRBool aForceBuffering)
|
||||
{
|
||||
if (mStream) {
|
||||
mStream->Resume();
|
||||
|
|
|
@ -219,7 +219,7 @@ class nsWaveDecoder : public nsMediaDecoder
|
|||
// Resume any media downloads that have been suspended. Called by the
|
||||
// media element when it is restored from the bfcache. Call on the
|
||||
// main thread only.
|
||||
virtual void Resume();
|
||||
virtual void Resume(PRBool aForceBuffering);
|
||||
|
||||
// Calls mElement->UpdateReadyStateForData, telling it which state we have
|
||||
// entered. Main thread only.
|
||||
|
|
Загрузка…
Ссылка в новой задаче