I have a node.js server converting and streaming mp3's on the fly. I am using an HTML5 audio tag to consume this stream, and the issue I'm having is the audio element doesn't know the duration of the mp3 until it has finished playing the whole thing (obviously). Is there any way, since my server knows the duration of the mp3 before sending it, that I can include the duration in the header of the response from the server or something, so the client consuming it knows the duration?
Thanks
To stream an mp3 file, upload your mp3 file to your public-html directory (or to a subdirectory of your public-html directory) preferably in MP3 format. OGG is also supported in Chrome and Firefox, but not in Safari and Internet Explorer. For example: Create a subdirectory (e.g., media) in your public-html directory.
HTML5 features include native audio and video support without the need for Flash. The HTML5 <audio> and <video> tags make it simple to add media to a website. You need to set src attribute to identify the media source and include a controls attribute so the user can play and pause the media.
There are three supported audio formats in HTML: MP3, WAV, and OGG.
Definition and Usage The currentTime property sets or returns the current position (in seconds) of the audio/video playback. When setting this property, the playback will jump to the specified position.
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.
I think for the time-being, in terms of a widely-supported solution, you're stuck sending that stuff through a separate request (or otherwise within some sort of manifest of your links -- included in the playlist you use to point at appropriate URIs, for example). This, of course, could be generated by your server, dynamically, when the songs are first added, and then served statically from then, on.
In a media-player which I'm building, I'm doing something similar to this -- downloading a json file which contains what would typically be within .mp3 metadata (which will also support non-mp3 versions, et cetera).
This is far from ideal, but again is one of those things that html5 audio/video wasn't meant to solve in its first iteration.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With