Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

audio duration in Safari always returning infinity when streaming through PHP

Tags:

javascript

php

For some reason in Safari (and no other major browser), when I serve an MP3 via PHP through an Audio context in JavaScript, the duration of the MP3 is always returned as infinity.

This problem has stumped me for the last few days and after reading a few links (including this one) in search for a solution, I have not progressed at all.


My code

PHP:

$path = "path/to/file.mp3";
$file = [
    "path"      => $path,
    "size"      => filesize($path),
    "bitrate"   => $bitrate
];

header("Accept-Ranges: bytes", false);
header("Content-Length: " . $file["size"], false);
header("Content-Type: audio/mpeg", false);

echo file_get_contents($file["path"]);

exit;

JavaScript:

var audio = new Audio(url);
// returns infinite on Safari
// returns 312.27311 on Chrome and Firefox (which is correct)
console.log(audio.duration);

I'm still yet to figure out why this problem is only in Safari and what is causing it in the first place, so if anyone has a solution it would be much appreciated!

Cheers.

like image 929
GROVER. Avatar asked Apr 07 '19 09:04

GROVER.


2 Answers

After much investigation like yourself I finally figured it out.

The first request Safari sends has the header:

Range: bytes=0-1

It's your job to respond "correctly" so that Safari will send additional requests to fetch the rest of the file.

Here is my example of the "correct" headers:

Content-Length: 2
Accept-Ranges: bytes
Content-Range: bytes 0-1/8469
(Make sure you set response status to 206!)
(8469 is the Content-Length of the entire file)

What happens after that is somewhat magical - Safari sends a follow up request that has this header:

Range: bytes=0-8468

And you should respond "correctly" like with these headers:

Content-Length: 8469
Accept-Ranges: bytes
Content-Range: bytes 0-8468/8469
(Again, status 206!)

I hope this solves it for you, since I spent many hours searching to no avail. I eventually realized I needed to adjust my server to handle requests where the Range header is present.

I used this commit to help me understand what the "correct" response is: https://github.com/jooby-project/jooby/commit/142a933a31b9d8742714ecd38475d90e563314f7

like image 121
brianarpie Avatar answered Sep 19 '22 00:09

brianarpie


As this thread is popping up first in google, allow me to share my experience.

Like many, I'm generating the mp3 through a php page, mostly in order to "hide" the real link of the mp3. And for this, Safari is quite annoying. To make it short, Safari considers the sound as a "live broadcast", ending up having this "infinite" duration. For the details, this post explained quite well why.

So what to do ? You have to put conditions on the headers of your php page that generate the sound (as mentioned by @brianarpie). Here is my php-generating-mp3 page :

<?php
// first, you need this function :
function serveFilePartial($fileName, $fileTitle = null, $contentType = 'application/octet-stream'){
// the code of this can be found here : https://github.com/pomle/php-serveFilePartial/blob/master/ServeFilePartial.inc.php
}
$link_of_your_mp3 = "var/my_folder/my_music.mp3"; // this is not an URL, but an absolute path
// and we call the function :
serveFilePartial($link_of_your_mp3, null, 'audio/mpeg');

?>

.. and that's it ! this function will generate the right headers depending on what the web-browser is asking for.

Of course you've done all your checking on link, vars, etc to protect the page. Note that my example is mp3, so I have to call the function with 'audio/mpeg'. If you output a different fileformat, you will have to adapt the content Type of the function.

like image 24
Fenix Aoras Avatar answered Sep 20 '22 00:09

Fenix Aoras