Bug 755533 - Ensure we fire canplaythrough if the media's channel is suspended before metadata is loaded. r=roc

This commit is contained in:
Chris Pearce 2012-05-28 10:40:06 +12:00
parent e57dc383ab
commit 0c2ab7bae9
3 changed files with 64 additions and 15 deletions

View File

@ -159,6 +159,10 @@ public:
// (no data has arrived for a while).
void DownloadStalled();
// Called by the media decoder to indicate whether the media cache has
// suspended the channel.
void NotifySuspendedByCache(bool aIsSuspended);
// Called when a "MozAudioAvailable" event listener is added. The media
// element will then notify its decoder that it needs to make a copy of
// the audio data sent to hardware and dispatch it in "mozaudioavailable"
@ -833,7 +837,7 @@ protected:
// due to loading a preload:none media. When true, the resource we'll
// load when the user initiates either playback or an explicit load is
// stored in mPreloadURI.
bool mLoadIsSuspended;
bool mSuspendedForPreloadNone;
// True if a same-origin check has been done for the media element and resource.
bool mMediaSecurityVerified;
@ -843,6 +847,9 @@ protected:
// True if the media has an audio track
bool mHasAudio;
// True if the media's channel's download has been suspended.
bool mDownloadSuspendedByCache;
};
#endif

View File

@ -593,7 +593,8 @@ void nsHTMLMediaElement::AbortExistingLoads()
mSuspendedAfterFirstFrame = false;
mAllowSuspendAfterFirstFrame = true;
mHaveQueuedSelectResource = false;
mLoadIsSuspended = false;
mSuspendedForPreloadNone = false;
mDownloadSuspendedByCache = false;
mSourcePointer = nsnull;
// TODO: The playback rate must be set to the default playback rate.
@ -905,7 +906,7 @@ void nsHTMLMediaElement::LoadFromSourceChildren()
void nsHTMLMediaElement::SuspendLoad()
{
mLoadIsSuspended = true;
mSuspendedForPreloadNone = true;
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
ChangeDelayLoadStatus(false);
@ -913,8 +914,9 @@ void nsHTMLMediaElement::SuspendLoad()
void nsHTMLMediaElement::ResumeLoad(PreloadAction aAction)
{
NS_ASSERTION(mLoadIsSuspended, "Can only resume preload if halted for one");
mLoadIsSuspended = false;
NS_ASSERTION(mSuspendedForPreloadNone,
"Must be halted for preload:none to resume from preload:none suspended load.");
mSuspendedForPreloadNone = false;
mPreloadAction = aAction;
ChangeDelayLoadStatus(true);
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
@ -987,7 +989,7 @@ void nsHTMLMediaElement::UpdatePreloadAction()
mPreloadAction = nextAction;
if (nextAction == nsHTMLMediaElement::PRELOAD_ENOUGH) {
if (mLoadIsSuspended) {
if (mSuspendedForPreloadNone) {
// Our load was previouly suspended due to the media having preload
// value "none". The preload value has changed to preload:auto, so
// resume the load.
@ -1001,7 +1003,7 @@ void nsHTMLMediaElement::UpdatePreloadAction()
} else if (nextAction == nsHTMLMediaElement::PRELOAD_METADATA) {
// Ensure that the video can be suspended after first frame.
mAllowSuspendAfterFirstFrame = true;
if (mLoadIsSuspended) {
if (mSuspendedForPreloadNone) {
// Our load was previouly suspended due to the media having preload
// value "none". The preload value has changed to preload:metadata, so
// resume the load. We'll pause the load again after we've read the
@ -1650,10 +1652,11 @@ nsHTMLMediaElement::nsHTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo)
mHasPlayedOrSeeked(false),
mHasSelfReference(false),
mShuttingDown(false),
mLoadIsSuspended(false),
mSuspendedForPreloadNone(false),
mMediaSecurityVerified(false),
mCORSMode(CORS_NONE),
mHasAudio(false)
mHasAudio(false),
mDownloadSuspendedByCache(false)
{
#ifdef PR_LOGGING
if (!gMediaElementLog) {
@ -1733,7 +1736,7 @@ NS_IMETHODIMP nsHTMLMediaElement::Play()
nsresult rv = Load();
NS_ENSURE_SUCCESS(rv, rv);
}
if (mLoadIsSuspended) {
if (mSuspendedForPreloadNone) {
ResumeLoad(PRELOAD_ENOUGH);
}
// Even if we just did Load() or ResumeLoad(), we could already have a decoder
@ -2636,6 +2639,20 @@ void nsHTMLMediaElement::FirstFrameLoaded(bool aResourceFullyLoaded)
mPreloadAction == nsHTMLMediaElement::PRELOAD_METADATA) {
mSuspendedAfterFirstFrame = true;
mDecoder->Suspend();
} else if (mLoadedFirstFrame &&
mDownloadSuspendedByCache &&
mDecoder &&
!mDecoder->IsEnded()) {
// We've already loaded the first frame, and the decoder has signalled
// that the download has been suspended by the media cache. So move
// readyState into HAVE_ENOUGH_DATA, in case there's script waiting
// for a "canplaythrough" event; without this forced transition, we will
// never fire the "canplaythrough" event if the media cache is so small
// that the download was suspended before the first frame was loaded.
// Don't force this transition if the decoder is in ended state; the
// readyState should remain at HAVE_CURRENT_DATA in this case.
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
return;
}
}
@ -2734,6 +2751,7 @@ void nsHTMLMediaElement::PlaybackEnded()
void nsHTMLMediaElement::SeekStarted()
{
DispatchAsyncEvent(NS_LITERAL_STRING("seeking"));
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
FireTimeUpdate(false);
}
@ -2746,6 +2764,11 @@ void nsHTMLMediaElement::SeekCompleted()
AddRemoveSelfReference();
}
void nsHTMLMediaElement::NotifySuspendedByCache(bool aIsSuspended)
{
mDownloadSuspendedByCache = aIsSuspended;
}
void nsHTMLMediaElement::DownloadSuspended()
{
DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
@ -2786,6 +2809,21 @@ void nsHTMLMediaElement::UpdateReadyStateForData(NextFrameStatus aNextFrame)
return;
}
if (mReadyState > nsIDOMHTMLMediaElement::HAVE_METADATA &&
mDownloadSuspendedByCache &&
mDecoder &&
!mDecoder->IsEnded()) {
// The decoder has signalled that the download has been suspended by the
// media cache. So move readyState into HAVE_ENOUGH_DATA, in case there's
// script waiting for a "canplaythrough" event; without this forced
// transition, we will never fire the "canplaythrough" event if the
// media cache is too small, and scripts are bound to fail. Don't force
// this transition if the decoder is in ended state; the readyState
// should remain at HAVE_CURRENT_DATA in this case.
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
return;
}
if (aNextFrame != NEXT_FRAME_AVAILABLE) {
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
if (!mWaitingFired && aNextFrame == NEXT_FRAME_UNAVAILABLE_BUFFERING) {

View File

@ -647,11 +647,15 @@ void nsBuiltinDecoder::NotifySuspendedStatusChanged()
MediaResource* activeStream;
bool suspended = mResource->IsSuspendedByCache(&activeStream);
if (suspended && mElement) {
// if this is an autoplay element, we need to kick off its autoplaying
// now so we consume data and hopefully free up cache space
mElement->NotifyAutoplayDataReady();
}
if (mElement) {
if (suspended) {
// If this is an autoplay element, we need to kick off its autoplaying
// now so we consume data and hopefully free up cache space.
mElement->NotifyAutoplayDataReady();
}
mElement->NotifySuspendedByCache(suspended);
mElement->UpdateReadyStateForData();
}
}
void nsBuiltinDecoder::NotifyBytesDownloaded()