Audio stream for radio application

Playing an MP3 stream seems like it should be a straightforward task that the Cocoa APIs would handle easily. Unfortunately, the normal approach to handling media (i.e. “Let Quicktime Handle It”) fails here — a quick attempt to play streaming MP3s in Apple’s QTKitPlayer example results in a few seconds of no response, followed by a “-2048” error.


There is probably a way to make QTKit play streaming MP3s. I decided to go a different way instead.

In Mac OS X 10.5 (Snowless Leopard), Apple introduced the AudioToolbox framework which contains the Audio File Stream Services and Audio Queue Services that we’ll use to solve the problem. These are pure C APIs: not as clean and simple to use as the Cocoa Objective-C APIs, but once written, should get the job done.

Audio File Stream reads the raw bytes and finds audio packets within them. The Audio Queue takes packets and plays them on the sound hardware. Between the two, they should handle streaming playback.



At the moment, the AudioToolbox doesn’t have any beginner-friendly “Guide” documentation. The only detailed introduction is the afsclient.cpp file in the AudioFileStreamExample that Apple provide (you’ll find it in /Developer/Examples/CoreAudio/Services/AudioFileStreamExample).

Sadly, this example is missing a few things to make it work as a proper player:

  • Doesn’t wait for audio to finish (the program quits when the data finishes loading, not when it finishes playing)
  • Never plays the final audio buffer
  • Only plays variable bit-rate data
  • Doesn’t provide “hints” about the data format (many file types won’t be recognized)

Addressing the issues

Waiting until the playback is finished

Immediately after the Audio Queue is created in the MyPropertyListenerProc, you can add a listener to the kAudioQueueProperty_IsRunning property of the Audio Queue. This will allow us to determine when playback has started and (more importantly) when playback has properly finished.

// listen to the “isRunning” property err = AudioQueueAddPropertyListener(myData->audioQueue, kAudioQueueProperty_IsRunning, MyAudioQueueIsRunningCallback, myData); if (err) { PRINTERROR(“AudioQueueAddPropertyListener”); myData->failed = true; break; }

With this in place, we can implement the MyAudioQueueIsRunningCallback function and use it to wait until the audio has finished playing before we exit the program.

Play the final buffer

This is a simple problem. All that is needed is to call the MyEnqueueBuffer function once more, after the data has finished loading, to ensure that the buffer in progress is sent to the Audio Queue. It may help to flush the queue as well.

Handle CBR data too

The “for CBR data, you’d need another code branch here” comment in the AudioFileStreamExample is a bit of a giveaway on this point. Basically, the code which follows that comment should be wrapped in an “if (inPacketDescriptions)” conditional, followed by an else that looks like this:

Straightforward stuff: you just copy all the data into the buffer, without needing to worry about packet sizes.

Hinting about data types

This is a bit more of an open ended problem. A few different approaches can work here:

  • Use file extensions to guess the file type
  • Use mime types provided in HTTP headers to determine the file type
  • Continuously invoke AudioFileStreamParseBytes on the first chunk of the file until it returns without an error
  • Hardcode the type, if you can presume it in all cases

I only implemented the first of these options. If you know the URL of the source file, it goes a little something like this:


Then you pass the fileTypeHint into the call to AudioFileStreamOpen.



Output of the Application


Grab the sample code from here.

This entry was posted in iPhone Development by Nimit Parekh. Bookmark the permalink.

About Nimit Parekh

Hey ! I am Nimit S. Parekh a passionate mobile application developer from India.. I am always hunting for better knowledge in iOS. I am looking forward to be a good app & game developer. I keep on sharing my knowledge @ as soon as I get spare time for it. I would love to hear your personal feedback about this blog.

Leave a Reply

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