Year an a half old, but I just ran into this last week so...
Add 206 Partial Content support to your http server implementation, and this problem goes away. Seeking within the content starts working too..
The audio and video html5 tags are making Range requests:
Range: bytes=0- or..
Range: bytes=0-12345
The important bits of the proper response from the spec:
HTTP/1.1 206 PARTIAL CONTENT
Accept-Ranges: bytes
Content-Range: bytes 0-12345
Without the 206 response code, you will get the behavior you are experiencing.
I do this in Perl like so, where $request contains the request headers from the client, normalized. For example, SERVER_PROTOCOL in most cases contains 'HTTP/1.1'
my $crlf = "\012";
if ( $request->{RANGE} && $request->{RANGE} =~ /bytes=(\d*)-(.*)$/ ) {
$offset = $1;
$end = $2 || $size; # end is optional in the spec. Default to size.
$header = $request->{SERVER_PROTOCOL} . ' 206 PARTIAL CONTENT' . $crlf .
'Content-Range: bytes ' . $offset . '-' . $end . '/' . $size . $crlf;
} else {
$header = $request->{SERVER_PROTOCOL} . ' 200 OK' . $crlf;
}
my $left = $end - $offset;
$header .= 'Server: ' . $SERVER_NAME . $crlf .
'Accept-Ranges: bytes' . $crlf .
'Date: ' . $http_date . $crlf .
'Content-Type: ' . ($self->simplemime($raw_path) || magic($fh)) . $crlf .
'Last-Modified: ' . $http_date . $crlf .
'Content-Length: ' . $size . $crlf .
'Connection: Keep-Alive' . $crlf .
'Cache-Control: max-age=' . $EXPIRE . $crlf . $crlf;
You then of course must honor the request by delivering the appropriate range of bytes for the requested content.
The client will often also 'stall' the download to match the speed of playback, so a proper event driven server such as Mojolicious, AnyEvent or Node.js will scale, whereas a 1 thread per connection model such as PHP does not. (well I suppose Ratchet would with some hacking, or using Xsendfile)
Incidentally, most Range requests end up being just:
Range: bytes=0-
That translates to as long as they can't seek and caching is disabled (and the browser actually honors it..), you can actually get away with just rewriting the header of a normal HTTP/1.1 200 response to the important bits of the HTTP/1.1 206 response and it works for some content. Specifically, this seems to work for content that does not have required metadata at the end of the file. For those file-types I've seen Range requests which seek to the end, and then restart at the beginning of the file.
For this reason, it is better to just implement the actual seeking, but the above does work... Use at your own peril.