Reading SHOUTcast metadata from a stream

A few people have looked at the source of my Indie 103.1 iPhone App and asked about reading SHOUTcast metadata (artist and track information) from the audio stream. The Indie App code won’t read metadata for SHOUTcast streams – but it does work for Icecast streams. Turns out that SHOUTcast and Icecast (two of the most popular server applications for streaming radio) are supposed to be compatible, but the response message from each server is slightly different.

SmackFu has a great page that documents the ShoutCAST metatdata protocol. They don’t mention the difference in the server response, but you’ll find references to it on Google.

When  you send an HTTP GET request to an Icecast server (like Indie 103.1) it replies with HTTP 200 OK. This is what we’d expect, and you can use CFHTTPMessageCopyHeaderFieldValue to read the response header where you get the Icy-MetaInt value. SHOUTcast responds to an HTTP GET request with ICY 200 OK. When this happens CFHTTPMessageCopyHeaderFieldValue returns null.

One solution is to look for the ICY 200 OK response and parse the headers manually. I’ve created another Google Code project called audiostreamer-meta and used Matt Gallagher’s execelent AudioStreamer example as a sample project. The sample application will parse the metadata from both server streams. Check the debug window in Xcode to see the metadata. The code needs some clean up, and it would be nice to display the artist, tract, and other metadata on the view itself. Feel free to join the project and help make the code better.

About mike

Mike Jablonski is a web/software/mobile developer who lives and works in Los Angeles, California.
This entry was posted in software and tagged . Bookmark the permalink.

33 Responses to Reading SHOUTcast metadata from a stream

  1. Don S. says:

    Mike, I’ve written an iPhone Radio Streaming player using the article on Matt Gallagher’s blog. I added your metadata code to ReadStreamCallback and the data is read perfectly. The only problem is that it introduced sound artifacts into the stream. If I remove all the metadata code, the stream sounds fine. Have you seen this problem? If so, how do you fix it? I’m very experienced programming in flex/actionscript/C++, but I’m fairly new to Objective-C. Otherwise, I probably would have an easier time finding the exact issue. Thanks.

  2. Don S. says:

    Wow, I actually just checked your google code page and saw that you had updated the metadata reading algorithm since I grabbed it last. I tried it out and there are no more artifacts. Thank you for the work and time you put into this. It will no doubt be helpful to many programmers struggling with this same issue. Thanks.

  3. mike says:

    That’s great! Thanks for letting me know you were able to use the code.

  4. teany says:

    Hi,

    Thank you for a nice project, but why do I get this in my console?

    26.05.09 20.57.32 Indie 103.1[31217] MetaData: CY 200 OK
    icy-notice1:This stream requires Winamp
    icy-notice2:SHOUTcast Distributed Network Audio Server/Linux v1.9.8
    icy-name:
    icy-genre: Underground
    icy-url:
    content-type:audio/mpeg
    icy-pub:1
    icy-metaint:32768
    icy-br:128

    ï,–™¥îâLMæQóàïN+FL’Zö‹;≠ì÷P…h≤€≤u¬Àu,‚ñ¢Ôõ؈Øö€QRL`#±ç7Æ`pÑÑ™à1Í¢à3áπ≈Ú€U1ÏŒ⁄G¯sfláÀç√À8—™ñÎH˙4H˝JÚ4“√âorÍE©°m•]o»’„≥æÜWSôX¥°‚(¬
    ïavDÑPá;ñ´”3àÈR”,ŒéÓûÕzı€IleôBIÕ+DeÇ%≤{ud®±6V®Ê—ÂoùınRú”Iıñóå⁄[`‘ø^m±åàÄÖÉχ5XQ•)}u”…E:™[T’Ü¡j4O ‹7w¨têflk›ßÓX§&êØ$6Ÿ†æ=+JOß≥º\£xTÖ,hÓ%ÒZ{êB¬∞*|¸ëØJÖ€éN0QtÕ∏∫‘o¨àÒ3Bã¥ÅVàµN(Ÿaô#6Ÿ9¸z®˜≈M‹gí∞öÚ&ÜóÏBFí Jnˇ˚ê`ô—g[·ÔKh@ÀÆ0ÂÈëqáΩ-®2Ç.ê∞§ Uh.¶≥≠Fü@w†JôgnkJ2ûÕ%fè‘#êH §Xc(l«˙¿≈II-2ª•8™SLe<9†∏†j˚fi AM›Ó eÓÀ‘_v!f«¢‹˜‘+Ë@j =û/6∏,ˆ‚lªfl#”f¡e’ñZX™ÁÒòÃO∫dÍ≥AXPA™§M©}ïa„“ë3Ÿˇ§s4y™⁄fi¬“+QÜ€ø

  5. mike says:

    Hm. I’m not sure. What’s the URL of the station you are trying to play? Are you using the Indie 103.1 code or the new audiostreamer-meta project code?

  6. teany says:

    I am using the indie code, the stream is http://s2.viastreaming.net:7130/

  7. dimix says:

    Hi, I’ve the same problem of teany. There is a solution? There is a different URL to use?

  8. teany says:

    I have switched to the new project, the meta data works, now I just need to figure out how to port the GUI elements from indie to this one, can you please point me in the right direction ?

  9. teany says:

    Good news, if I took the AudioStream.m & .h files from the new project and put them in the Indie code, it works, but it also try to push out the steaming url in the LABEL field .

    Do you know why?

  10. mike says:

    The metadata in the stream can include a stream URL as well as the stream title. You’ll need to parse the string to remove what you don’t want. The string normally looks something like this:

    StreamTitle='Artist - Track';StreamURL='http://someurl.com';

  11. teany says:

    Hi,

    Thank you for the help, it works perfect now, but I have no ideá how i “parse” the meta data. Do you have any hits? maybe you know about a book etc?

  12. teany says:

    Hi, again.

    Has it something whit hte c1 = c2 = ?

  13. teany says:

    Thank you.

  14. mike says:

    Blaenk – I saw that as well. One of these days I’ll have to update my project fork with his new code. Thanks!

  15. Blaenk says:

    Hey mike, I was playing around with Matt’s source yesterday and I wanted metadata fetching so I decided to ‘manually merge’ the source files. I’m no expert so I probably screwed some stuff up, despite double/triple checking (The error handling is different in the new one, there’s one instance where I commented out a ‘failed = YES’ statement).

    I’m using it right now and it works well. I was listening to a 256k stream on my wifi for about an hour and it was silky smooth and great. Sometimes, not too often, the audio gets kind of static-y. If this were due to latency I wouldn’t mind as I realize there’s little I can do, however, I’m afraid it might have to do with memory or something? Like I said, I’m no expert, maybe you can take a look around. One thing I did notice in Matt’s new version is that the buffers are now 32 instead of 6, or something.

    Here are the two updated source files: http://www.blaenkdenum.com/downloads/AudioStreamer-latest+icymeta.zip

  16. mike says:

    Blaenk – did you use the latest code for the metadata logic? I checked in a bug fix on June 11th.

  17. mike says:

    Blaenk – looks like you have the latest in the zip. Do you get the same problems using the old version of Matt’s code – or just with the new version? What’s the stream url?

  18. Blaenk says:

    I think it was due to the 256kbit nature of the stream. After trying a 96k stream it was pretty much flawless. The stream was a premium stream from http://www.di.fm. And I’m not positive on the old/new version of Matt’s code causing the same problem. I /believe/ that it didn’t happen before, then again I probably didn’t run it long enough to encounter it. Any ideas?

    Also, I have a general question about Matt’s code. I’ll ask it on his page as well but I figured I’d ask you in case you knew. The pause toggle method, from my tests at runtime it seems like it literally pauses the stream, meaning when I unpause, the audio resumes where it left off. Does it do this by simply accumulating the ongoing stream into the buffers? Does this mean that if I leave it paused for a long time it will eventually run out of memory? I tried it and then after resuming, the stream had a few artifacts for a few seconds then the stream just stopped (the state became isIdle I believe).

    Which brings me to my next question. When the stream gets interrupted does it try to reconnect? I’ll try and look through the code for this one, was just wondering though because it seems like an effort is made, or something, but nothing happens.

  19. mike says:

    Blaenk – I’m not sure why the 256k stream would cause problems… You can try to play with the number of buffers and size of each to see if that helps.

    The AudioQueuePause documentation from Apple says pausing doesn’t “affect the buffers or reset the audio queue”, but I’m not sure what they mean by that exactly. I don’t think I would want to pause a live stream anyway, I’d just stop and re-start it.

    The interrupt event has never consistently worked for me either. Sometimes the app and stream will resume, sometimes my app will quit and I’ll end up in the contacts app, etc.

  20. Bob says:

    Just wanted to say a BIG thank you for your code. I have learned a TON and today was able to learn more about NSStream and NSArray to get my non-ID3 metadata parsing the way I want with your code base. Now all I need to do is learn more so I can create the other “pages” i need :)

  21. mike says:

    @Bob Cool! Glad to hear it.

  22. Bogdan says:

    Hello, Mike, and congratulations for the great work!

    I just wanted to ask if you updated the source code with the latest from Matt.

    Thanks!

  23. mike says:

    @Bogdan I have not. But @Blaenk in comment #52 did. I think his link is dead, but you can try to ask him via his blog.

  24. Bogdan says:

    Ok, I will, thank you!
    Another short question, maybe you can help me: do you an idea how to get the bitrate of the stream? I was trying with
    AudioFileStreamGetProperty(audioFileStream, kAudioFileStreamProperty_BitRate, &bitRateSize, &bitRate);
    but I keep getting the error “Value not available”.

    Thank you!

  25. mike says:

    @Bogdan That’s how you get the bitrate, just make sure you look for it in “MyPacketsProc” and not “MyPropertyListenerProc”.

  26. Bogdan says:

    I noticed that in the latest code form Matt, he obtains the bitrate just as I was trying to. The thing is that I was trying to get the bitrate when using an AAC stream, and it seems it works only with MP3 streams.

    Because I wanted to use this latest version of Matt’s, I added myself the metainfo code of yours and it works ok. I also commented the fail_with_error code when trying to read the bitrate, because when using AAC streams, the play failed.

    In a week or so I will publish this code on the internet (I still need to polish it a bit – for instance, error handling), if you agree, and I will add the link here also.

  27. mike says:

    @Bogdan For some streams the only way I’ve gotten the bitrate is to look for “icy-br” in the response headers the same way you look for “icy-metaint”.

    That would be great if you publish the code, thanks!

  28. sj says:

    so I am trying to use this with a shoutcast stream, and I keep getting really different/really strange results (I have been able to stream just fine with the old matt gallagher example). Some of the times this does work, I get the metadata from the stream etc, but then the audio loops through the same segment twice and ends. Other times I just get an EXC_BAD_ACCESS error in MyPacketsProc() when it tries to copy to the audio queue buffer, when this happens the metadata (if any is retrieved) is just garbled nonsense. This can take a few seconds of streaming for it to happen but it always seems to terminate with EXC_BAD_ACCESS and produced a lot of nonsense. I am using the latest release right now. Any help would be much appreciated and thank you for all of your work!!!

  29. sj says:

    also, when it does go through and all, no skips/repeats it stops after about 10 seconds

  30. mike says:

    What’s the URL of the stream you are using?

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>