1

I'm trying to stream an mp3 file with PHP and play it on the browser. I'm using Ubuntu for both the server ( apache ) and client for testing. My code works on Chrome, but not on FireFox. When I access the mp3 directly ( so it's served by the web server ) it works on FireFox as well, but comparing the headers that the web server generates with the headers I send in PHP I couldn't find how to fix the problem. ( I'm spying the headers using FireBug )

Here are the webserver generated headers ( That does work ):

Accept-Ranges   bytes
Connection  Keep-Alive
Content-Length  490265
Content-Type    audio/mpeg
Date    Sun, 11 Mar 2012 04:01:45 GMT
Etag    "22064e-77b19-4badff4a88200"
Keep-Alive  timeout=5, max=100
Last-Modified   Sat, 10 Mar 2012 09:15:52 GMT
Server  Apache/2.2.20 (Ubuntu)

Here are the headers that are sent to the browser from my PHP script:

Accept-Ranges   bytes
Cache-Control   no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection  Keep-Alive
Content-Length  490265
Content-Type    audio/mpeg
Date    Sun, 11 Mar 2012 04:16:00 GMT
Expires Thu, 19 Nov 1981 08:52:00 GMT
Keep-Alive  timeout=5, max=100
Pragma  no-cache
Server  Apache/2.2.20 (Ubuntu)
X-Powered-By    PHP/5.3.6-13ubuntu3.6

This is the code I use to stream the mp3:

header('Content-length: ' . filesize($path));
header('Content-Type: audio/mpeg');
header('Accept-Ranges: bytes');

readfile($path);

exit;

I did also tried other headers which didn't help, such as:

header('Content-Disposition: inline; filename="name.mp3"');
header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
header('Pragma: no-cache');
header('Cache-Control: no-cache');

But like I said, none of these fixed the problem.

Many thanks for any help,

Oded.

EDIT:

OK this appears to be extremely strange. After much debugging, I made sure that the headers and content of the PHP version and the webserver versions are the same, and then I found out what breaks it, but I have no idea why. Here is the scenario that breaks it:

  • 1) Store a string of a path in $_SESSION in a previous script.
  • 2) Read this string in the script that streams the mp3.
  • 3) Use this string as the path to load the mp3 file.

If I do that, FireFox cannot play the file, when I press on the mp3 player, it prints a "GstDecodeBin2: This appears to be a text file" message. If I hard code the path instead of using the $_SESSION, it works. The crazy thing is that I made absolutely sure that the path in the $_SESSION is correct! Remember that the headers and content of the PHP and webserver versions are identical!

2 Answers2

0

The HTTP Accept-Ranges header allows the browser to send a starting and ending point of the file to download, this allows for multi-part downloading of the same file. There are plenty of PHP implementations of this, here is one found on the PHP.net documentation page for fread().

http://www.php.net/manual/en/function.fread.php#106999

Ben Phelps
  • 196
  • 4
  • 8
  • Thanks, this is important information, but did not fix the problem, The browser does not request ranges in this case. I suspect the problem may be with the cache control, as you can see there are some differences in the headers that PHP sends: it sends "Cache-Control no-store, no-cache, must-revalidate, post-check=0, pre-check=0", and "Pragma no-cache". I will try to somehow prevent PHP from sending these headers. – Oded V Streigold Mar 11 '12 at 21:47
  • I edited the question because the comment was too long, please see above. – Oded V Streigold Mar 15 '12 at 03:42
0

I found what the problem is using WireShark to monitor the requests. Earlier I used FireBug and HTTPFox, and they don't show all the requests!

WireShark showed me that after the initial successful request there is another request for the same URI. This second request was not caught by xdebug, and was missed by FireBug, and HTTPFox. The problem is that this request does not include the PHPSESSID! Obviously as a result the session did not work, and because it did work on the first request I was confused. This seems to me like a bug in FireFox with its media player module. I can work around this by manually adding the PHPSESSID to the URL as query string.