Skip to content

Streaming Shoutcast on the iPhone

UPDATE 11/5/07 After inserting that first line of code

set_time_limit(10 * 60);

I seem to have less choppy music. It turns out my server only allowed any PHP script to run for only 60 seconds. This disconnected Safari, and initiated another request from it…causing the overlapping music problem I explained below. I haven’t had time to fully test it, but in theory setting a longer time limit would tone this problem down quite a bit. Ideally, you’d give Safari every byte it asked for, and not bail out because the script is taking too long.


The first post in a brand new blog!

I frequently listen to a certain online radio station, Cerritos All Stars. Since I’m always around the house listening to music on my iPhone, I wanted a way to listen to the station instead of just my music library. Of course I started Google searching ASAP. I found several things:

  • There is no Shoutcast client for the iPhone yet
  • iPhone’s Safari can play mp3 files which are located online, but not Shoutcast streams

On a Macrumors forum, some people were talking about tricking Safari into thinking that a Shoutcast stream is just a plain mp3 file on a web server. The method they tried was using PHP to fake some HTTP headers: Content-type, Content-Length. After that, just read bytes from the Shoutcast server and pass them on to Safari. This almost works. Safari brings starts up the built-in Quicktime player and seems ready to play, but instead the “play” symbol with a strikeout line through it shows up.

Further on in the forum thread, someone mentioned that Safari downloads the mp3 in chunks. This is to enable simultaneous playing and buffering. This is why the aforementioned method did not work.

  • Safari first requests the script
  • The script sends mp3 type headers, Safari likes them and starts up its Quicktime player
  • Safari asks for bytes 0 and 1 of the mp3, to test for file-resuming capabilities
  • The script sends out the same headers
  • Safari thinks, “impostor!”

So after doing a little reading about HTTP responses and all that good stuff, I realized that the PHP script needed to be a little more flexible. It needed to pretend to be resuming a file transfer when Safari asked it to. So a little modifying, and bingo.

  • Safari first requests the script
  • The script sends mp3 type headers, Safari likes them and starts up its Quicktime player
  • Safari asks for bytes 0 and 1 of the mp3, to test for file-resuming capabilities
  • The script sends HTTP 206 and related headers
  • The script sends two bytes of the Shoutcast stream
  • Safari thinks, “sweet, gimme more!”
  • Safari asks for bytes 0 - x
  • The script sends more bytes of the stream
  • Quicktime plays bytes of the stream

The quick and dirty code:

set_time_limit(10 * 60); //this could take a while, allowing 10 minutes. just added recently see UPDATE at the top of post

$bytes_to_send = 480000 * 130; //stream about about 2 hours of music

$headers = http_get_request_headers(); //get the HTTP request headers safari has sent

//if safari is only asking for a portion of the "mp3"
if (isset($headers['Range'])) {
	$exploded_range = explode('=', $headers['Range']);
	$limits = explode('-', $exploded_range[1]);
	$length = ($limits[1] - $limits[0]) + 1; //the content length
	$content_range = 'bytes ' . $limits[0] . '-' . $limits[1]; //the content range

	//send fake HTTP headers to safari, telling it that we're sending only the portion of the "mp3" it asked for
	header('HTTP/1.1 206 Partial Content');
	header('Accept-Ranges: bytes');
	header('Content-Length: ' . $length);
	header('Content-Range: ' . $content_range . '/' . $bytes_to_send);
	header('Content-type: audio/mpeg');

	//open the stream to the shoutcast server, set as resource $fp
	$fp = fsockopen("stream.cerritosallstars.com", "80", $errno, $errstr, 30) or die("Unable to connect to server!");

	//HTTP commands that will initiate the shoutcast server sending stream data
	$buf = "GET / HTTP/1.0\r\nIcy-MetaData:0\r\n\r\n";

	//send HTTP commands in string $buf to stream $fp
	fwrite($fp, $buf);
	//get next line from stream
	$buf = fgets($fp, 1024);		

	//get next few lines and discard them, this is only
	//shoutcast data that would sound like noise if iphone played them
	$buf = fgets($fp, 1024);
	$buf = fgets($fp, 1024);
	$buf = fgets($fp, 1024);
	$buf = fgets($fp, 1024);
	$buf = fgets($fp, 1024);
	$buf = fgets($fp, 1024);
	$buf = fgets($fp, 1024);
	$buf = fgets($fp, 1024);

	//break if EOF
	if ($buf == "\r\n") {
		break;
	}

	$bytes_sent = 0;

	//while pointer is not at EOF, and not too many bytes are sent...
	while (!feof($fp) AND ($bytes_sent < $length)) {
		//read 1 byte of stream
		$buf = fread($fp, 1);

		//output byte to iphone;
		echo $buf;
		$bytes_sent++;
	}
	fclose($fp);
	exit();
}

//else, it is the initial request. safari is asking for the whole "mp3", and seeing how big it is ($bytes_to_send)
else {
	header('Accept-Ranges: bytes');
	header('Content-Length: ' . $bytes_to_send);
	header('Content-type: audio/mpeg');

	echo 'blah';
	exit();
}
exit();

So in the end, music played. However, this is a pretty sketchy way of doing things. Because of the way Quicktime buffers, seconds of music are not heard, and seconds of music are repeated. Know what I mean? Every request that happens at the same time will be filled with the same byte. The script can definitely get bytes from the present fine but when Safari requests bytes from the future, it still gets bytes from the present. Here is another way to view the issue.

  • Safari asks for bytes 1 - 3
  • Script serves bytes 1, 2, 3 and Safari plays them
  • While playing byte 2, Safari asks for bytes 4 - 6
  • CRAP, byte 3 will be the same as byte 4

A few seconds of repeated music are heard. I can live with this, but theres no way any final solution could have this problem.

I would also like to point out that PHP and Apache have been ported to the iPhone. There might be a way to run the script locally on the iPhone, eliminating the need for another server and extra bandwidth. And another reminder, this would only be ideal on WiFi…iPhone can’t make or receive calls while using EDGE.

If anyone has an elegant way of listening to Shoutcast on iPhone, please leave a comment!

{ 35 } Comments

  1. michael | November 27th, 2007 at 2:34 pm | Permalink

    this helped me out immensely! THANKS!

  2. Vaughn | November 30th, 2007 at 5:07 am | Permalink

    Conceited Software has concocted a Shoutcast client called iRadio (EDGE/WIFI; mpeg-only so far)

  3. Trent | December 2nd, 2007 at 9:32 am | Permalink

    Yes, iRadio is fantastic. However, I still can’t choose to listen to any stream I want.

  4. Mrsan | January 10th, 2008 at 1:05 pm | Permalink

    Im having trouble with this.

    i get the player to start, but i dont get more then a play arrow that has a circle and a line over it

  5. Mrsan | January 11th, 2008 at 12:52 pm | Permalink

    What extension do you have loaded to your php when u run this?

  6. Trent | February 4th, 2008 at 4:50 am | Permalink

    You need the HTTP extension to run this code

  7. Daniel | February 5th, 2008 at 1:36 pm | Permalink

    Hello, you are fantastic. I found your script an love it. Everything is fine with mp3streaming.

    I try to use the script for streaming a aac stream. I’t doesn’t work.

    Everyone have an idea?

    Greets
    Daniel

  8. Russell Harrower | February 6th, 2008 at 6:36 am | Permalink

    need help i get this error
    Parse error: syntax error, unexpected T_IF in /home/queer/public_html/iphone.php on line 7

    <?php
    set_time_limit(10 * 60); //this could take a while, allowing 10 minutes. just added recently see UPDATE at the top of post
    $bytes_to_send = 480000 * 130; //stream about about 2 hours of music
    $headers = http_get_request_headers(); //get the HTTP request headers safari has sent

    //if safari is only asking for a portion of the “mp3″
    if (isset($headers['Range'])) {
    $exploded_range = explode(’=', $headers['Range']);
    $limits = explode(’-', $exploded_range[1]);
    $length = ($limits[1] - $limits[0]) + 1; //the content length
    $content_range = ‘bytes ‘ . $limits[0] . ‘-’ . $limits[1]; //the content range

    //send fake HTTP headers to safari, telling it that we’re sending only the portion of the “mp3″ it asked for
    header(’HTTP/1.1 206 Partial Content’);
    header(’Accept-Ranges: bytes’);
    header(’Content-Length: ‘ . $length);
    header(’Content-Range: ‘ . $content_range . ‘/’ . $bytes_to_send);
    header(’Content-type: audio/mpeg’);

    //open the stream to the shoutcast server, set as resource $fp
    $fp = fsockopen(”stream.cerritosallstars.com”, “80″, $errno, $errstr, 30) or die(”Unable to connect to server!”);

    //HTTP commands that will initiate the shoutcast server sending stream data
    $buf = “GET / HTTP/1.0\r\nIcy-MetaData:0\r\n\r\n”;

    //send HTTP commands in string $buf to stream $fp
    fwrite($fp, $buf);
    //get next line from stream
    $buf = fgets($fp, 1024);

    //get next few lines and discard them, this is only
    //shoutcast data that would sound like noise if iphone played them
    $buf = fgets($fp, 1024);
    $buf = fgets($fp, 1024);
    $buf = fgets($fp, 1024);
    $buf = fgets($fp, 1024);
    $buf = fgets($fp, 1024);
    $buf = fgets($fp, 1024);
    $buf = fgets($fp, 1024);
    $buf = fgets($fp, 1024);

    //break if EOF
    if ($buf == “\r\n”) {
    break;
    }

    $bytes_sent = 0;

    //while pointer is not at EOF, and not too many bytes are sent…
    while (!feof($fp) AND ($bytes_sent

  9. Trent | February 6th, 2008 at 3:30 pm | Permalink

    @Russell: Try erasing the comments in the code. If that doesn’t work, let me think about it more

  10. Russell | February 7th, 2008 at 8:37 am | Permalink

    @Trent, i tryed everything, am website is hosted on a linux server… I tryed the script in all my web browsers and iphone and ipod touch.

    PLZ HELP

  11. russell | February 11th, 2008 at 7:37 am | Permalink

    @Trent, do you have any idea? why it not working?

  12. Trent | February 15th, 2008 at 2:08 am | Permalink

    @Russel: I’ve been extremely busy with work and school lately… I will find time to work this out asap. My apologies

  13. Russell | February 22nd, 2008 at 5:07 pm | Permalink

    @Trent: thx

  14. Bill | February 25th, 2008 at 1:29 pm | Permalink

    first off, I love concept of this code. However, I can’t get it to work.

    are you running this on PHP4 or php5?

    all of my servers are php4 and I think that may have something to do with it. with a little tweaking I was able to get it to load quicktime on my phone, but it keeps saying “error playing movie”

  15. Bill | February 26th, 2008 at 7:16 am | Permalink

    I got it to work!!!

    I works fine with my shoutcast stream from Spacial, but my other streams through limelight won’t load. Is there some problem opening a long url with slashes in it? Other than that I can’t seem to find any reasons why it would not load.

    Any ideas?

  16. Russell | February 28th, 2008 at 8:04 pm | Permalink

    @Bill: Can you post what you did to get the code to work.

  17. Bill | March 1st, 2008 at 5:38 am | Permalink

    Russell, it took some hair pulling, but I believe that this is the course of events that lead to it working.

    first, I built a dedicated test server, because I couldn’t get my netsol, or godaddy hosting to work.

    The server is running Fedora Core 2, has XAMPP installed, and I made sure that the PECL_HTTP extensions were installed.

    Next, none of my systems would recognize the function http_get_request_headers() so I changed that to apache_request_headers()

    and then it worked. I also got my limelight streams to work by contacting limelight and getting the exact port, not the proxy that they gave me to stream over port 80, (in this case port 80 didn’t work)

    So now all my streams are working great. I did have to tweek it a bit to get it to play for more than 10 minutes. My latest version of it will play for 14 hours.

    I have had a 96k and 128k stream play great over EDGE with no buffering even while driving around.

    I am not in my office right now, I will try to post a link to my tweeked code next week.

    If you would like to see it in action you can hear one of my streams at …

    http://www.allpetsradio.com/iphone

    just click on “listen live”

    I love this code!!!!

  18. Tom | March 5th, 2008 at 3:13 am | Permalink

    Bill, if you could, it would be great to share the source of what you’ve done.

    It would help me a lot!

    Thanks. :)

  19. Russell | March 6th, 2008 at 4:24 am | Permalink

    @Bill: Like what you have done, seem that changing http_get_request_headers() to apache_request_headers()

    works fine however have some problems
    1. My Ipod Touch is saying “Server is not responding”

    However i know my stream is working fine, as i am listening to it at home. (Stream is in a office in the City i live north of the city)

    Iam using Shoutcast

    Any idea

  20. Tom | March 6th, 2008 at 12:25 pm | Permalink

    Russel, may I ask what host you’re using?

    Thanks, Tom.

  21. Russell | March 6th, 2008 at 6:04 pm | Permalink

    I am using Cyberspace - cyberspace.net.au

    General server information:
    Operating system Linux
    Service Status Click to View
    Kernel version 2.6.23.12-ts.grh.mh.i386
    Machine Type i686
    Apache version 1.3.37 (Unix)
    PERL version 5.8.8
    Path to PERL /usr/bin/perl
    Path to sendmail /usr/sbin/sendmail
    Installed Perl Modules Click to View
    PHP version 5.2.3
    MySQL version 4.1.22-standard
    cPanel Build 11.18.2-RELEASE 21594
    Theme cPanel X v2.6.0
    Documentation Click to View
    cPanel Pro 1.0 (RC1)

  22. richman | March 9th, 2008 at 1:35 pm | Permalink

    Hmmmm….. I seem to be having a problem with this http://joystickradio.com/stream.php?server=joystickradio.com&port=8000
    any ideas?

  23. Gast00n | March 24th, 2008 at 12:01 am | Permalink

    You have to install the pecl_http extension to your php :

    for php5 :

    apt-get install php5-dev (to enable pecl command)
    apt-get install libcurl3 (to get curl support)
    apt-get install libmagic
    (perhaps some differences for different distributions)

    pecl install pecl_http

    cp /usr/lib/php5/20060613+lfs/http.so /usr/lib/apache2/modules

    then add to your php.ini “extension=http.so”

    and restart apache

  24. Deepak | April 4th, 2008 at 11:34 pm | Permalink

    This script is great. It works. I use it to stream shoutcast teluguone radio to iphone. Still not able to resolve the 1-2 second repetitions. Any ideas on how to resolve this??

    I don’t have PECL_HTTP extensions so modified the script to use basic server variables

    foreach($_SERVER as $key_name => $key_value)
    $headers[$key_name] = $key_value ;

    Once you have all the keys just use $headers['HTTP_RANGE'] and it works

    thanks for the great script nakasaki and bill your allpetsradio is an inspiration for writing my radio page.

  25. smake | April 15th, 2008 at 2:22 am | Permalink

    It works great! I use a Mac OS X 10.3.9 as Server (Apache/1.3.33 (Darwin) PHP/4.3.10) and the
    $headers = apache_request_headers();

    http://212.201.36.171/stream3000.php

  26. simon smith | May 2nd, 2008 at 8:46 am | Permalink

    Good concept, but how do you deal with the metadata embedded in the shoutcast stream? The script looks like it will just pass that to iphone and im not sure the players can handle that. What this means is every 32768 bytes of data, your stream will sound like it clicks, pops or stutters (as the metadata, raw text, is buried in the mp3 stream. Thats not so in regular files or other streaming methods). Maybe people here have noticed it and just figured it was an artifact of wireless or something. Some info on it here http://www.smackfu.com/stuff/programming/shoutcast.html

  27. Georgios | May 4th, 2008 at 12:54 pm | Permalink

    Hello guys i am realy not very good on php and i need some help to make this work, if you think you can help me please send me an email. indefixx@gmail.com

    Thanks in advance.

  28. rotot | June 23rd, 2008 at 7:10 pm | Permalink

    for the script php , where you need to insert your url , try to withdraw the “http://”
    just insert the dns or ip adress

  29. bryan | October 17th, 2008 at 10:38 am | Permalink

    thanks for the script… works beautifully. i just wish safari on iphone would let you switch tabs while running quicktime, so i could control my MPD stream thru a web UI…

  30. bramsyuur | November 14th, 2008 at 9:36 pm | Permalink

    I’ve implemented this code and works great! Thanks.
    Only I’ve noticed about one issue:
    - When you press pause, and turn off your iphone, the streaming, seems, that are storing a “buffer” under the server… later, when you turn on your iphone, the streaming continues on the same point where you have been paused.

    Can some one test these issue?

    Only I’ve made tests on 3G not Wi-Fi.

    Thanks.

  31. Abhi | December 9th, 2008 at 12:27 pm | Permalink

    Awesome code… works like a charm… awesome job. I just had to use Bill’s fix, since I am running Apache too, and it worked!!!! :D

    Thanks a ton….

  32. Mario Di Vece | December 24th, 2008 at 4:56 pm | Permalink

    Awesome stuff… Thanks for the info.

  33. raziel | January 6th, 2009 at 11:58 am | Permalink

    hi i try but i have this error

    Fatal error: Call to undefined function http_get_request_headers() in /home/afterco/public_html/stream.php on line 4

    any can help me
    thanks.

  34. Bull | February 6th, 2009 at 3:04 am | Permalink

    Great job. Thanks your. :)

  35. hot sister incest | June 29th, 2009 at 6:38 am | Permalink

    I should email u about it.

{ 25 } Trackbacks

  1. Besser als iRadio | mlife.at | April 15th, 2008 at 11:24 am | Permalink

    [...] -> Streaming Shoutcast on the iPhone [...]

  2. [...] a traditional broadcast station. However, for those who have more time to configure their service, there are ways to stream live music using QuickTime. Apparently the Tuner application in the app store will [...]

  3. quality casino online | March 17th, 2009 at 12:34 am | Permalink

    quality casino online…

    t845t…

  4. Gay Atlanta | April 6th, 2009 at 5:33 pm | Permalink

    Gay Atlanta…

    t1000t…

  5. Free Online Personals China Friends | April 6th, 2009 at 5:42 pm | Permalink

    Free Online Personals China Friends…

    t136t…

  6. Gay Ballbusting | April 6th, 2009 at 5:59 pm | Permalink

    Gay Ballbusting…

    t662t…

  7. Free Personals Online Dating Services | April 6th, 2009 at 6:10 pm | Permalink

    Free Personals Online Dating Services…

    t300t…

  8. Gay Bangkok Hotel | April 6th, 2009 at 6:15 pm | Permalink

    Gay Bangkok Hotel…

    t633t…

  9. Free Personals Trial Yahoo | April 6th, 2009 at 6:19 pm | Permalink

    Free Personals Trial Yahoo…

    t274t…

  10. Gay Bar Directory | April 6th, 2009 at 6:26 pm | Permalink

    Gay Bar Directory…

    t568t…

  11. Free Phone Dating Services | April 6th, 2009 at 6:29 pm | Permalink

    Free Phone Dating Services…

    t994t…

  12. Gay Barcelona | April 6th, 2009 at 6:42 pm | Permalink

    Gay Barcelona…

    t870t…

  13. Free Porn Of Gay Guys In Jockstraps Having Sex…

    t922t…

  14. Gay Bareback Anal Sex | April 6th, 2009 at 6:48 pm | Permalink

    Gay Bareback Anal Sex…

    t483t…

  15. Free Single Parent Dating Personals | April 6th, 2009 at 7:16 pm | Permalink

    Free Single Parent Dating Personals…

    t532t…

  16. Free Singles Dating | April 6th, 2009 at 7:20 pm | Permalink

    Free Singles Dating…

    t872t…

  17. Gay Bars Charleston Sc | April 6th, 2009 at 7:25 pm | Permalink

    Gay Bars Charleston Sc…

    t916t…

  18. Free Swingers Personals Uk | April 6th, 2009 at 7:53 pm | Permalink

    Free Swingers Personals Uk…

    t36t…

  19. Free Teen Dating Chat | April 6th, 2009 at 7:58 pm | Permalink

    Free Teen Dating Chat…

    t573t…

  20. Gay Locker Room Stories Gay Stories | April 7th, 2009 at 9:21 pm | Permalink

    Gay Locker Room Stories Gay Stories…

    t809t…

  21. Michigan Swingers | April 7th, 2009 at 9:22 pm | Permalink

    Michigan Swingers…

    t336t…

  22. Gay London Massage | April 7th, 2009 at 9:31 pm | Permalink

    Gay London Massage…

    t3t…

  23. Military Dating | April 7th, 2009 at 10:15 pm | Permalink

    Military Dating…

    t793t…

  24. Gay Male Naturists | April 7th, 2009 at 11:32 pm | Permalink

    Gay Male Naturists…

    t977t…

  25. Miss Gay | April 7th, 2009 at 11:35 pm | Permalink

    Miss Gay…

    t800t…

Post a Comment

Your email is never published nor shared.