Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

audio.duration returns Infinity on Safari when mp3 is served from PHP

So, I have a php script that sends my mp3 file to the audio tag of html5. The problem is that in Safari, the audio.duration tag does not work and returns infinity. If I set the src of the audio directly to the file everything works fine. But I don't want my users to see the path to the file.

Anyways, this is how I'm sending my headers from the PHP.

I have already tried having the content-ranges. That did not help.

if (file_exists($filename)) {
  $fp = fopen($filename, 'r');
  $etag = md5(serialize(fstat($fp)));
  fclose($fp);
  header("Content-Transfer-Encoding: binary"); 
  header("Content-Type: audio/mpeg");
  header('Content-Length: ' . (string)(filesize($filename)));
  header('Content-Disposition: inline; filename="' . $filename . '"');
  header('X-Pad: avoid browser bug');
  header('Cache-Control: no-cache');
  header('Etag: ' . $etag);

  //GetContentRange($filelength);

  readfile($filename);
  exit;
}
else {
  header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found', true, 404);
  echo "no file";
}
like image 746
Roozbeh15 Avatar asked Mar 09 '12 05:03

Roozbeh15


3 Answers

I was having the same issue and added a couple headers. Seems to work for me -

 header("Pragma: public");
 header("Expires: 0"); 
 header("Content-Type: audio/mpeg");
 header('Content-Length: ' . $fsize);
 header('Content-Disposition: inline; filename="' . $track2play . '"');
 header( 'Content-Range: bytes 0-'.$shortlen.'/'.$fsize); 
 header( 'Accept-Ranges: bytes');
 header('X-Pad: avoid browser bug');
 header('Cache-Control: no-cache');
 header('Etag: ' . $etag);

$fsize is file size and $shortlen =$filesize-1...tested on Safari 5.1.4 and on new iPad...

like image 94
Michael Penfield Avatar answered Oct 18 '22 03:10

Michael Penfield


Your server must enable Range requests. This is easy to check for by seeing if your server's response includes the Accept-Ranges in its header. Most HTML5 browsers enable seeking to new file positions during a download, so the server must allow the new Range to be requested.

Failure to accept byte Range requests will cause problems on some HTML5 browsers. Often the duration cannot be read from the file as some formats require that the start and end of the file is read to know its duration. Chrome tends to be the browser that has most problems if the Range request is not enabled on the server, but all browsers will have some issue even if it is only that you have to wait for all the media to load before jumping close to the end.

See this post for a PHP example of how to serve media with Range Requests enabled.

Also, i noticed you are using No-Cache..

Using the server response to disable the local cache of media can cause problems with some HTML5 browsers. This can cause the duration of the media to be unknown.

like image 24
Lloyd Avatar answered Oct 18 '22 01:10

Lloyd


  • Today i wasted most of my time looking into this issue and finally came out with an solution. The reason behind why safari returns duration as infinity is quite interesting: It appears that Safari requests the server twice for playing files. First it sends a range request to the server with a range header like this:(bytes:0-1).
  • If the server doesn't return the response as a partial content and if it returns the entire stream then the safari browser will not set audio.duration tag and which result in playing the file only once and it can't be played again.
  • I made an quick fix to it, in the server side just check the header and if it contains a rangeHeader then return it.

    String rangeHeader = request.getHeader("Range"); if(rangeHeader != null) { //just return it }

like image 30
smurf Avatar answered Oct 18 '22 01:10

smurf