mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 543769 - Buffer non-autobuffer videos upon first playback to ensure smooth playback. r=roc a=blocking2.0
This commit is contained in:
parent
e3f81960a0
commit
6aa46b1e86
@ -1010,7 +1010,7 @@ void nsHTMLMediaElement::StopSuspendingAfterFirstFrame()
|
|||||||
return;
|
return;
|
||||||
mSuspendedAfterFirstFrame = PR_FALSE;
|
mSuspendedAfterFirstFrame = PR_FALSE;
|
||||||
if (mDecoder) {
|
if (mDecoder) {
|
||||||
mDecoder->Resume();
|
mDecoder->Resume(PR_TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1662,11 +1662,6 @@ PRBool nsHTMLMediaElement::ShouldCheckAllowOrigin()
|
|||||||
PR_TRUE);
|
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)
|
void nsHTMLMediaElement::UpdateReadyStateForData(NextFrameStatus aNextFrame)
|
||||||
{
|
{
|
||||||
if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
|
if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
|
||||||
@ -1677,8 +1672,6 @@ void nsHTMLMediaElement::UpdateReadyStateForData(NextFrameStatus aNextFrame)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
|
|
||||||
|
|
||||||
if (aNextFrame != NEXT_FRAME_AVAILABLE) {
|
if (aNextFrame != NEXT_FRAME_AVAILABLE) {
|
||||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
|
||||||
if (!mWaitingFired && aNextFrame == NEXT_FRAME_UNAVAILABLE_BUFFERING) {
|
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
|
// make a real estimate, so we go straight to HAVE_ENOUGH_DATA once
|
||||||
// we've downloaded enough data that our download rate is considered
|
// we've downloaded enough data that our download rate is considered
|
||||||
// reliable. We have to move to HAVE_ENOUGH_DATA at some point or
|
// 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 :
|
if (stats.mTotalBytes < 0 ? stats.mDownloadRateReliable :
|
||||||
stats.mTotalBytes == stats.mDownloadPosition) {
|
stats.mTotalBytes == stats.mDownloadPosition ||
|
||||||
|
mDecoder->CanPlayThrough())
|
||||||
|
{
|
||||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
||||||
return;
|
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);
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1951,7 +1936,7 @@ void nsHTMLMediaElement::NotifyOwnerDocumentActivityChanged()
|
|||||||
mDecoder->Pause();
|
mDecoder->Pause();
|
||||||
mDecoder->Suspend();
|
mDecoder->Suspend();
|
||||||
} else {
|
} else {
|
||||||
mDecoder->Resume();
|
mDecoder->Resume(PR_FALSE);
|
||||||
if (!mPaused && !mDecoder->IsEnded()) {
|
if (!mPaused && !mDecoder->IsEnded()) {
|
||||||
mDecoder->Play();
|
mDecoder->Play();
|
||||||
}
|
}
|
||||||
|
@ -446,7 +446,8 @@ NS_IMETHODIMP nsBuiltinDecoder::Observe(nsISupports *aSubjet,
|
|||||||
nsMediaDecoder::Statistics
|
nsMediaDecoder::Statistics
|
||||||
nsBuiltinDecoder::GetStatistics()
|
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;
|
Statistics result;
|
||||||
|
|
||||||
MonitorAutoEnter mon(mMonitor);
|
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.");
|
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||||
if (mStream) {
|
if (mStream) {
|
||||||
mStream->Resume();
|
mStream->Resume();
|
||||||
}
|
}
|
||||||
|
if (aForceBuffering) {
|
||||||
|
MonitorAutoEnter mon(mMonitor);
|
||||||
|
mDecoderStateMachine->StartBuffering();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsBuiltinDecoder::StopProgressUpdates()
|
void nsBuiltinDecoder::StopProgressUpdates()
|
||||||
|
@ -293,6 +293,12 @@ public:
|
|||||||
// Only called on the decoder thread. Must be called with
|
// Only called on the decoder thread. Must be called with
|
||||||
// the decode monitor held.
|
// the decode monitor held.
|
||||||
virtual void UpdatePlaybackPosition(PRInt64 aTime) = 0;
|
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
|
class nsBuiltinDecoder : public nsMediaDecoder
|
||||||
@ -393,7 +399,7 @@ class nsBuiltinDecoder : public nsMediaDecoder
|
|||||||
// Resume any media downloads that have been suspended. Called by the
|
// Resume any media downloads that have been suspended. Called by the
|
||||||
// media element when it is restored from the bfcache. Call on the
|
// media element when it is restored from the bfcache. Call on the
|
||||||
// main thread only.
|
// main thread only.
|
||||||
virtual void Resume();
|
virtual void Resume(PRBool aForceBuffering);
|
||||||
|
|
||||||
// Tells our nsMediaStream to put all loads in the background.
|
// Tells our nsMediaStream to put all loads in the background.
|
||||||
virtual void MoveLoadsToBackground();
|
virtual void MoveLoadsToBackground();
|
||||||
|
@ -817,29 +817,7 @@ nsresult nsBuiltinDecoderStateMachine::Run()
|
|||||||
// There is at most one frame in the queue and there's
|
// 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
|
// more data to load. Let's buffer to make sure we can play a
|
||||||
// decent amount of video in the future.
|
// decent amount of video in the future.
|
||||||
if (IsPlaying()) {
|
StartBuffering();
|
||||||
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"));
|
|
||||||
} else {
|
} else {
|
||||||
if (mBufferExhausted) {
|
if (mBufferExhausted) {
|
||||||
// This will wake up the decode thread and force it to try to
|
// This will wake up the decode thread and force it to try to
|
||||||
@ -943,10 +921,12 @@ nsresult nsBuiltinDecoderStateMachine::Run()
|
|||||||
case DECODER_STATE_BUFFERING:
|
case DECODER_STATE_BUFFERING:
|
||||||
{
|
{
|
||||||
TimeStamp now = TimeStamp::Now();
|
TimeStamp now = TimeStamp::Now();
|
||||||
if (now - mBufferingStart < TimeDuration::FromSeconds(BUFFERING_WAIT) &&
|
nsMediaStream* stream = mDecoder->GetCurrentStream();
|
||||||
mDecoder->GetCurrentStream()->GetCachedDataEnd(mDecoder->mDecoderPosition) < mBufferingEndOffset &&
|
if (!mDecoder->CanPlayThrough() &&
|
||||||
!mDecoder->GetCurrentStream()->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
|
now - mBufferingStart < TimeDuration::FromSeconds(BUFFERING_WAIT) &&
|
||||||
!mDecoder->GetCurrentStream()->IsSuspendedByCache()) {
|
stream->GetCachedDataEnd(mDecoder->mDecoderPosition) < mBufferingEndOffset &&
|
||||||
|
!stream->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
|
||||||
|
!stream->IsSuspendedByCache()) {
|
||||||
LOG(PR_LOG_DEBUG,
|
LOG(PR_LOG_DEBUG,
|
||||||
("In buffering: buffering data until %d bytes available or %f seconds",
|
("In buffering: buffering data until %d bytes available or %f seconds",
|
||||||
PRUint32(mBufferingEndOffset - mDecoder->GetCurrentStream()->GetCachedDataEnd(mDecoder->mDecoderPosition)),
|
PRUint32(mBufferingEndOffset - mDecoder->GetCurrentStream()->GetCachedDataEnd(mDecoder->mDecoderPosition)),
|
||||||
@ -1273,3 +1253,32 @@ void nsBuiltinDecoderStateMachine::LoadMetadata()
|
|||||||
return;
|
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 ClearPositionChangeFlag();
|
||||||
virtual void SetSeekable(PRBool aSeekable);
|
virtual void SetSeekable(PRBool aSeekable);
|
||||||
virtual void UpdatePlaybackPosition(PRInt64 aTime);
|
virtual void UpdatePlaybackPosition(PRInt64 aTime);
|
||||||
|
virtual void StartBuffering();
|
||||||
|
|
||||||
|
|
||||||
// Load metadata Called on the state machine thread. The decoder monitor must be held with
|
// 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);
|
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
|
// media element when it is restored from the bfcache, or when we need
|
||||||
// to stop throttling the download. Call on the main thread only.
|
// to stop throttling the download. Call on the main thread only.
|
||||||
// The download will only actually resume once as many Resume calls
|
// The download will only actually resume once as many Resume calls
|
||||||
// have been made as Suspend calls.
|
// have been made as Suspend calls. When aForceBuffering is PR_TRUE,
|
||||||
virtual void Resume() = 0;
|
// 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,
|
// Returns a weak reference to the media element we're decoding for,
|
||||||
// if it's available.
|
// if it's available.
|
||||||
@ -234,6 +236,10 @@ public:
|
|||||||
float aPixelAspectRatio,
|
float aPixelAspectRatio,
|
||||||
Image* aImage);
|
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:
|
protected:
|
||||||
|
|
||||||
// Start timer to update download progress information.
|
// Start timer to update download progress information.
|
||||||
|
@ -1639,7 +1639,7 @@ nsWaveDecoder::Suspend()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsWaveDecoder::Resume()
|
nsWaveDecoder::Resume(PRBool aForceBuffering)
|
||||||
{
|
{
|
||||||
if (mStream) {
|
if (mStream) {
|
||||||
mStream->Resume();
|
mStream->Resume();
|
||||||
|
@ -219,7 +219,7 @@ class nsWaveDecoder : public nsMediaDecoder
|
|||||||
// Resume any media downloads that have been suspended. Called by the
|
// Resume any media downloads that have been suspended. Called by the
|
||||||
// media element when it is restored from the bfcache. Call on the
|
// media element when it is restored from the bfcache. Call on the
|
||||||
// main thread only.
|
// main thread only.
|
||||||
virtual void Resume();
|
virtual void Resume(PRBool aForceBuffering);
|
||||||
|
|
||||||
// Calls mElement->UpdateReadyStateForData, telling it which state we have
|
// Calls mElement->UpdateReadyStateForData, telling it which state we have
|
||||||
// entered. Main thread only.
|
// entered. Main thread only.
|
||||||
|
Loading…
Reference in New Issue
Block a user