Bug 479863. Stop buffering media elements by default, implement 'autobuffer' attribute to control buffering. r=doublec

--HG--
extra : rebase_source : 5fa0b63da981fdfcf7b1555174b6d1122bf2ae97
This commit is contained in:
Robert O'Callahan 2009-05-18 14:00:44 +12:00
Родитель ead1765b83
Коммит 40b4e3e4e9
9 изменённых файлов: 196 добавлений и 18 удалений

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

@ -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

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

@ -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;
};

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

@ -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()

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

@ -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,

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

@ -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 \

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

@ -0,0 +1,45 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=479863
-->
<head>
<title>Test for Bug 479863</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=479863">Mozilla Bug 479863</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<video id='v1' onerror="event.stopPropagation();"></video><audio id='a1' onerror="ev
ent.stopPropagation();"></audio>
<video id='v2' onerror="event.stopPropagation();" autobuffer></video><audio id='a2' on
error="event.stopPropagation();" autobuffer></audio>
<pre id="test">
<script type="application/javascript">
var v1 = document.getElementById('v1');
var a1 = document.getElementById('a1');
var v2 = document.getElementById('v2');
var a2 = document.getElementById('a2');
ok(!v1.autobuffer, "v1.autobuffer should be false by default");
ok(!a1.autobuffer, "v1.autobuffer should be false by default");
ok(v2.autobuffer, "v2.autobuffer should be true");
ok(a2.autobuffer, "v2.autobuffer should be true");
v1.autobuffer = true;
a1.autobuffer = true;
ok(v1.autobuffer, "video.autobuffer not true");
ok(a1.autobuffer, "audio.autobuffer not true");
is(v1.getAttribute("autobuffer"), "", "video autobuffer attribute not set");
is(a1.getAttribute("autobuffer"), "", "video autobuffer attribute not set");
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,73 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=479863
-->
<head>
<title>Test for Bug 479863</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="use_large_cache.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=479863">Mozilla Bug 479863</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<script type="application/javascript">
var stalls = { v1:false, v2:false, v3:false, v4:false, v5:false, v6:false };
var loads = { v1:false, v2:false, v3:false, v4:false, v5:false, v6:false };
var totalLoadEvents = 0;
var expectedLoadEvents = 6;
function loaded(event) {
loads[event.target.id] = true;
++totalLoadEvents;
if (totalLoadEvents == expectedLoadEvents) {
ok(stalls.v1, "v1 should stall");
// We might get accidental stalls on very slow machines, so lets not test these
// ok(!stalls.v2, "v2 should not stall");
// ok(!stalls.v3, "v3 should not stall");
ok(stalls.v4, "v4 should stall");
ok(stalls.v5, "v5 should stall");
ok(stalls.v6, "v6 should stall");
ok(loads.v1, "v1 should load after setting autobuffer");
ok(loads.v2, "v2 should load");
ok(loads.v3, "v3 should load");
ok(loads.v4, "v4 should load after calling play()");
ok(loads.v5, "v5 should load after seeking");
ok(loads.v6, "v6 should load after setting autoplay");
SimpleTest.finish();
}
}
function stalled(event) {
stalls[event.target.id] = true;
if (event.target.id == "v1") {
event.target.autobuffer = true;
} else if (event.target.id == "v4") {
event.target.play();
} else if (event.target.id == "v5") {
event.target.currentTime = 0.1;
} else if (event.target.id == "v6") {
event.target.autoplay = true;
}
}
</script>
<video id='v1' src="seek.ogv" onstalled="stalled(event)" onload="loaded(event)"></video>
<video id='v2' src="seek.ogv" onstalled="stalled(event)" onload="loaded(event)" autobuffer></video>
<video id='v3' src="seek.ogv" onstalled="stalled(event)" onload="loaded(event)" autoplay></video>
<video id='v4' src="seek.ogv" onstalled="stalled(event)" onload="loaded(event)"></video>
<video id='v5' src="seek.ogv" onstalled="stalled(event)" onload="loaded(event)"></video>
<video id='v6' src="seek.ogv" onstalled="stalled(event)" onload="loaded(event)"></video>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</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");
</script>
</pre>
</body>

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

@ -56,7 +56,7 @@
#endif
%}
[scriptable, uuid(0ccf2f96-af4c-4af2-b684-b28f3369827f)]
[scriptable, uuid(23fb201a-556d-4054-9885-6437e8910296)]
interface nsIDOMHTMLMediaElement : nsIDOMHTMLElement
{
// error state
@ -90,6 +90,7 @@ interface nsIDOMHTMLMediaElement : nsIDOMHTMLElement
readonly attribute boolean ended;
readonly attribute boolean mozAutoplayEnabled;
attribute boolean autoplay;
attribute boolean autobuffer;
void play();
void pause();