diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index a9ef184eaa8..71fe08cb8b9 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -115,6 +115,9 @@ GK_ATOM(attribute, "attribute") GK_ATOM(attributeSet, "attribute-set") GK_ATOM(aural, "aural") GK_ATOM(_auto, "auto") +#ifdef MOZ_MEDIA +GK_ATOM(autobuffer, "autobuffer") +#endif GK_ATOM(autocheck, "autocheck") GK_ATOM(autocomplete, "autocomplete") #ifdef MOZ_MEDIA diff --git a/content/html/content/public/nsHTMLMediaElement.h b/content/html/content/public/nsHTMLMediaElement.h index 8611c54b7a2..48a4ef65f13 100644 --- a/content/html/content/public/nsHTMLMediaElement.h +++ b/content/html/content/public/nsHTMLMediaElement.h @@ -311,6 +311,11 @@ protected: */ void ChangeDelayLoadStatus(PRBool aDelay); + /** + * If we suspended downloading after the first frame, unsuspend now. + */ + void StopSuspendingAfterFirstFrame(); + /** * Called when our channel is redirected to another channel. * Updates our mChannel reference to aNewChannel. @@ -435,4 +440,12 @@ protected: // PR_TRUE when we've got a task queued to call SelectResource(), // or while we're running SelectResource(). PRPackedBool mIsRunningSelectResource; + + // PR_TRUE if we suspended the decoder because we were paused, + // autobuffer and autoplay were not set, and we loaded the first frame. + PRPackedBool mSuspendedAfterFirstFrame; + + // PR_TRUE if we are allowed to suspend the decoder because we were paused, + // autobuffer and autoplay were not set, and we loaded the first frame. + PRPackedBool mAllowSuspendAfterFirstFrame; }; diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp index b3864f99d83..34cb3860a38 100644 --- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -296,6 +296,7 @@ NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement) NS_IMPL_URI_ATTR(nsHTMLMediaElement, Src, src) NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Controls, controls) NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Autoplay, autoplay) +NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Autobuffer, autobuffer) /* readonly attribute nsIDOMHTMLMediaElement mozAutoplayEnabled; */ NS_IMETHODIMP nsHTMLMediaElement::GetMozAutoplayEnabled(PRBool *aAutoplayEnabled) @@ -382,6 +383,8 @@ void nsHTMLMediaElement::AbortExistingLoads() mLoadedFirstFrame = PR_FALSE; mAutoplaying = PR_TRUE; mIsLoadingFromSrcAttribute = PR_FALSE; + mSuspendedAfterFirstFrame = PR_FALSE; + mAllowSuspendAfterFirstFrame = PR_TRUE; // TODO: The playback rate must be set to the default playback rate. @@ -646,6 +649,8 @@ NS_IMETHODIMP nsHTMLMediaElement::GetCurrentTime(float *aCurrentTime) NS_IMETHODIMP nsHTMLMediaElement::SetCurrentTime(float aCurrentTime) { + StopSuspendingAfterFirstFrame(); + if (!mDecoder) return NS_ERROR_DOM_INVALID_STATE_ERR; @@ -772,7 +777,9 @@ nsHTMLMediaElement::nsHTMLMediaElement(nsINodeInfo *aNodeInfo, PRBool aFromParse mIsRunningLoadMethod(PR_FALSE), mIsLoadingFromSrcAttribute(PR_FALSE), mDelayingLoadEvent(PR_FALSE), - mIsRunningSelectResource(PR_FALSE) + mIsRunningSelectResource(PR_FALSE), + mSuspendedAfterFirstFrame(PR_FALSE), + mAllowSuspendAfterFirstFrame(PR_TRUE) { RegisterFreezableElement(); } @@ -790,8 +797,21 @@ nsHTMLMediaElement::~nsHTMLMediaElement() } } +void nsHTMLMediaElement::StopSuspendingAfterFirstFrame() +{ + mAllowSuspendAfterFirstFrame = PR_FALSE; + if (!mSuspendedAfterFirstFrame) + return; + mSuspendedAfterFirstFrame = PR_FALSE; + if (mDecoder) { + mDecoder->Resume(); + } +} + NS_IMETHODIMP nsHTMLMediaElement::Play() { + StopSuspendingAfterFirstFrame(); + if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) { nsresult rv = Load(); NS_ENSURE_SUCCESS(rv, rv); @@ -859,16 +879,23 @@ nsresult nsHTMLMediaElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue, aNotify); - if (aNotify && - aNameSpaceID == kNameSpaceID_None && - aName == nsGkAtoms::src && - mLoadWaitStatus == WAITING_FOR_SRC_OR_SOURCE) - { - // A previous load algorithm instance is waiting on a src - // addition, resume the load. It is waiting at "step 1 of the load - // algorithm". - mLoadWaitStatus = NOT_WAITING; - QueueSelectResourceTask(); + if (aNotify && aNameSpaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::src) { + if (mLoadWaitStatus == WAITING_FOR_SRC_OR_SOURCE) { + // A previous load algorithm instance is waiting on a src + // addition, resume the load. It is waiting at "step 1 of the load + // algorithm". + mLoadWaitStatus = NOT_WAITING; + QueueSelectResourceTask(); + } + } else if (aName == nsGkAtoms::autoplay) { + StopSuspendingAfterFirstFrame(); + if (mReadyState == nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) { + NotifyAutoplayDataReady(); + } + } else if (aName == nsGkAtoms::autobuffer) { + StopSuspendingAfterFirstFrame(); + } } return rv; @@ -1217,6 +1244,15 @@ void nsHTMLMediaElement::FirstFrameLoaded() { ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA); ChangeDelayLoadStatus(PR_FALSE); + + NS_ASSERTION(!mSuspendedAfterFirstFrame, "Should not have already suspended"); + + if (mDecoder && mAllowSuspendAfterFirstFrame && mPaused && + !HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) && + !HasAttr(kNameSpaceID_None, nsGkAtoms::autobuffer)) { + mSuspendedAfterFirstFrame = PR_TRUE; + mDecoder->Suspend(); + } } void nsHTMLMediaElement::ResourceLoaded() diff --git a/content/media/video/public/nsMediaDecoder.h b/content/media/video/public/nsMediaDecoder.h index 75e3ee704c2..e7a9fe8b666 100644 --- a/content/media/video/public/nsMediaDecoder.h +++ b/content/media/video/public/nsMediaDecoder.h @@ -213,13 +213,16 @@ public: virtual void Shutdown(); // Suspend any media downloads that are in progress. Called by the - // media element when it is sent to the bfcache. Call on the main - // thread only. + // media element when it is sent to the bfcache, or when we need + // to throttle the download. Call on the main thread only. This can + // be called multiple times, there's an internal "suspend count". virtual void Suspend() = 0; // 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. + // 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; // Returns a weak reference to the media element we're decoding for, diff --git a/content/media/video/test/Makefile.in b/content/media/video/test/Makefile.in index 687f6d8a8b6..d16c63d21b4 100644 --- a/content/media/video/test/Makefile.in +++ b/content/media/video/test/Makefile.in @@ -46,6 +46,7 @@ include $(topsrcdir)/config/rules.mk _TEST_FILES = \ can_play_type_ogg.js \ can_play_type_wave.js \ + test_autobuffer.html \ test_autoplay.html \ test_can_play_type.html \ test_constants.html \ @@ -64,6 +65,7 @@ _TEST_FILES += \ dynamic_redirect.sjs \ test_access_control.html \ file_access_controls.html \ + test_autobuffer2.html \ test_bug448534.html \ test_bug461281.html \ test_bug468190.html \ diff --git a/content/media/video/test/test_autobuffer.html b/content/media/video/test/test_autobuffer.html new file mode 100644 index 00000000000..4bee15055db --- /dev/null +++ b/content/media/video/test/test_autobuffer.html @@ -0,0 +1,45 @@ + + + +
++ ++ + diff --git a/content/media/video/test/test_autobuffer2.html b/content/media/video/test/test_autobuffer2.html new file mode 100644 index 00000000000..7c48ecdb3f2 --- /dev/null +++ b/content/media/video/test/test_autobuffer2.html @@ -0,0 +1,73 @@ + + + + +
+ ++ + diff --git a/content/media/video/test/test_autoplay.html b/content/media/video/test/test_autoplay.html index 1df518eab42..8666712f051 100644 --- a/content/media/video/test/test_autoplay.html +++ b/content/media/video/test/test_autoplay.html @@ -21,9 +21,11 @@ ok(v2.autoplay, "v2.autoplay should be true"); ok(a2.autoplay, "v2.autoplay should be true"); v1.autoplay = true; -v1.autoplay = true; +a1.autoplay = true; ok(v1.autoplay, "video.autoplay not true"); -ok(v1.autoplay, "audio.autoplay not true"); +ok(a1.autoplay, "audio.autoplay not true"); +is(v1.getAttribute("autoplay"), "", "video autoplay attribute not set"); +is(a1.getAttribute("autoplay"), "", "video autoplay attribute not set");