Monday 8 November 2010

How to stop a video or audio element downloading

Say you've started playback of an HTML5 audio or video element, and you decide you really want to cancel playback and downloading of the media resource. Stopping playback is easy, just call the pause() method. But the network connection won't be stopped until the media element gets garbage collected, and even if you release all references to the media element, it won't be destroyed until the browser decides to runs its garbage collector. How can you stop the download of the media resource in the meantime? Here's a quick hack to achieve this: just reset the element's src attribute to the empty string. This destroys the element's internal decoder, and stops the network download.

For example:

<audio id="audiocontrols>
  <source src="funky-music.ogg">
  <source src="funky-music.mp3">
</audio>
<!-- ... Some time later, we decide we should stop the audio element playing and downloading... -->
<script>
var audio = document.getElementById("audio");
audio.pause();
audio.src = ""; // Stops audio download.
audio.load(); // Initiate a new load, required in Firefox 3.x.
</script>


Be aware that this will destroy the media element's decoders, so the element won't be playable anymore, and it will be rendered as an "error cross" if it's in a document. Also in Firefox 3.x you need to call load() after changing the source, whereas in Firefox 4 the load is scheduled to run when you change the src attribute, and the extra load() call is not required.

7 comments:

Unknown said...

Extra info:

Performing the src="" and .load() will cause an 'error' event to occur... Since it tries to load the current page into the media element.

If the download in in progress, the 'abort' event will also occur.

Unknown said...

This breaks IE9. IE9 raises an exception if you .load() any invalid media URL. The bug is in IE9 of course, since it should raise an error event instead.

The iPhone does not like this either, if used by itself to stop a download. The built in video player looses it's "done" button to close the player. Requires browser restart.

mike said...

Also this does not work so well if you want to just to pre-load 10 seconds of a clip and then stop loading the rest of the asset.

Anonymous said...

I am setting up a pay-per-stream feature so I have a heart-beat function that informs the server to continue a transfer. The transfer is paused whenever the heart stops beating.

Since the user is paying for what they buffer, I want to prevent the buffer from getting too far ahead.

I really would have liked to see a mediaElement.suspend() way of doing this.

In any case, I need to detect the current buffer length and the current position. I am using something like:
if (this.buffered.end(this.buffered.length-1) - this.currentTime > 2)
heartBeat.delay(100)

Since you implemented the .buffered attribute in Firefox - can you tell me why this always returns the end position of the ogv video, rather than the end of the buffered content? It seems like the current implementation is actually an implementation of mediaElement.seekable which Firefox is missing.

Chris Pearce said...

openid: There's currently no way to pause and resume the download from the client side. The method I describe in this blog post destroys your media element, so it cannot be resumed.

There's currently a discussion happening on the WhatWG mailing list about standardizing a way to control how far ahead of the current playback position the media buffers in this thread:
http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2011-January/029898.html

You may be interested in that.

Since you can control the server, maybe you should throttle the transmission of the media at 1.5 * (bitrate of media)? Then it won't get too far ahead?

Re: your use of buffered; note that multiple ranges can be buffered! When we load an Ogg video, if we can't predetermine the duration from an OggIndex or from a Content-Duration header, we'll seek to the end of the media and read a bit, and compare timestamps with the start of the media in order to calculate the duration. We display this as the duration of the media in the controls. This is why you're seeing the last bit of the media buffered.

If you play around with this demo:
http://people.mozilla.org/~cpearce/buffered-demo.html
You will see a visual representation of all ranges buffered in the media, which might make that clearer.

If you index your Ogg files (either by using OggIndex or by encoding with ffmpeg2theora v0.27 or newer), or if you serve your media with its duration in an X-Content-Duration header, you won't see that extra seek. See https://developer.mozilla.org/en/Configuring_servers_for_Ogg_media for more info.

gold programming said...

i personally prefer the jwplayer
a player for all season i must say simple and easily configurable

i am a web developer and i was assigned the task of developing a section where logged in users could watch videos and audio uploaded on site, client also requested that the users should not be able to download the videos

it was tedious and complicated but once it was done it was very fulfilling so i decided to write about it http://goldprogramming.blogspot.com/2011/11/secure-streaming-of-media-on-your-site.html

vixi said...

Thanks so much! This is really useful, and has saved my project, as I couldn't find anything online that covered this and it was a huge problem for me.

I haven't retested across browsers yet, but works great in Chrome 18.0.1025.168 on Mac.

Thanks again!