Realtime MP3 Decoding in Actionscript 3

So did you ever try to play Shoutcast streams in Flash? Did you run into memory leaks? Did the playback sound pitched or otherwise screwed? Fear no more. Let me introduce you to as3icy.

as3icy is a drop-in replacement for the Flash Player’s Sound object that can reliably play Shoutcast, Icecast and Limewire MP3 streams. And it extracts metadata info from the stream in real time. It also reliably plays VBR MP3.

DISCLAIMER IN CAPITAL LETTERS. This is experimental code. I basically threw this code together in two 24 hour coding sessions. Also, i know as much about sound engineering as i know about pottery – i blind-ported a big chunk of Java code (the excellent JLayer MP3 decoder, originally ported from C if i’m not mistaken) to Actionscript 3 without really knowing what i was doing. So expect a few WTF moments when reading through the code, and do not expect everything to work perfectly just yet.

Some known issues

  • IT IS SLOW AND STRESSES YOUR CPU. Depending on the encoding, i get around 20-50% load on my 2.4GHz Core 2 Duo (Vista), which is way too much. On my MacBook Air 2.16 GHz: full load and playback chokes, which is unacceptable. There is plenty of room for optimization though, the code is currently not optimized at all. It’s probably worth taking a look at Apparat, or Alchemy in general for more performant alternatives to decode MP3..
  • Only Layer III is supported (Layer I and II decoders not ported yet).
  • Only 44100Hz playback is supported (up/down-sampling to 44100Hz not implemented yet).
  • Only stereo modes are supported (mono not implemented yet).
  • The stream parser seems to be with problems yet, sometimes it happens that it runs out of sync for some reason.

Also, Adobe AIR is recommended to play Shoutcast streams, for two reasons: The meta data interval is passed back to the client via HTTP response headers, which are not available in the plugin version of the Flash Player, and as soon as you put the thing online you need the streaming server to host a crossdomain.xml file. If you happen to have access to the streaming server, you can of course solve these issues by hardcoding the meta data interval and have the streaming server serve an appropriate crossdomain.xml, or by using a proxy script of some sort. Also, this only applies to playing Shoutcast streams. You can play static MP3 files (for example VBR MP3s) using as3swf in the plugin version of the Flash Player without any problem (security restrictions still apply of course).

A simple example: Shoutcast stream playback

package 
{
  import com.codeazur.as3icy.ICYSound;
  import com.codeazur.as3icy.events.ICYMetaDataEvent;

  import flash.display.Sprite;
  import flash.events.HTTPStatusEvent;
  import flash.net.URLRequest;
  import flash.net.URLRequestHeader;
  
  public class SimpleShoutcastPlayer extends Sprite
  {
    protected static const URL:String = "http://72.233.14.70/hype";
    
    protected var icySound:ICYSound;
    
    public function SimpleShoutcastPlayer() 
    {
      var req:URLRequest = new URLRequest(URL);
      req.requestHeaders = [ new URLRequestHeader("Icy-Metadata", "1") ];
      icySound = new ICYSound();
      icySound.addEventListener(ICYMetaDataEvent.METADATA, metaDataHandler);
      icySound.addEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS, httpResponseStatusHandler);
      icySound.load(req);
      icySound.play();
    }
    
    protected function httpResponseStatusHandler(e:HTTPStatusEvent):void {
      // The HTTP_RESPONSE_STATUS event is fired when the stream is opened and starting to load.
      var s:String = "Connected!";
      if (icySound.icyName.length > 0) { s += "\rName: " + icySound.icyName; }
      if (icySound.icyDescription.length > 0) { s += "\rDescription: " + icySound.icyDescription; }
      if (icySound.icyUrl.length > 0) { s += "\rURL: " + icySound.icyUrl; }
      if (icySound.icyServer.length > 0) { s += "\rServer: " + icySound.icyServer; }
      if (icySound.icyMetaInterval > 0) { s += "\rMeta Interval: " + icySound.icyMetaInterval; }
      trace(s);
    }
    
    protected function metaDataHandler(e:ICYMetaDataEvent):void {
      // The METADATA event is fired when new metadata is available.
      if (e.metaData.length > 0) {
        trace("Now playing: " + e.metaData.slice(13, -2));
      }
    }
  }
}

This code (Adobe AIR required) plays the Hype Machine live radio stream, and traces out general information about the station, as well as the names of currently playing songs.

Note that we set a custom request header, Icy-Metadata: 1. This tells the server to periodically send meta data with the stream. Under the hood, as3icy transparently extracts this meta data from the stream in real time and passes it to the application via the ICYMetaDataEvent. This event won’t be fired when the request header above is not sent.

The trace output should look something like this:

Connected!
Name: Hype Radio
Description: Radio powered by music blogs
URL: http://hypem.com/radio/
Server: Icecast 2.3.1
Meta Interval: 16000
Now playing: Collider - Time Concerns
Now playing: Rogue Wave - Birds (Neil Young cover)
Now playing: The Big Pink – Dominos
[..]

13 thoughts on “Realtime MP3 Decoding in Actionscript 3

  1. This really works well! Do you need help completing this? Would love to get all formats working? If so let me know what you need.

  2. Shaun, sure, i would apprechiate that a lot! Although, as i already said in my post, non-native MP3 decoding is very heavy on the CPU, so i don’t know if this project will be useful at all

  3. What’s going on here? Is this going to happen? I downloaded your code and couldn’t get it to work, are there any compiler errors that I should be aware of? Obviously somebody made it work.

  4. I am unable to get VBR MP3’s to play using as3swf as you say. You have the error “This mp3 is encoded with variable bitrates. VBR is not allowed. Please use CBR mp3s” on line 146 of TagDefineSound. Am I missing something? Thanks!

  5. Patrick, i took out the check for VBR in TagDefineSound. I was under the false impression that the Flash Player doesn’t support VBR. It does. Thanks!

  6. Hi! I have some problems:
    my adobe cs5.5 compiler wrote me those errors:
    L:\Flash\Probes_works\Probe\claus-as3icy-6db44ab\source\com\codeazur\as3icy\ICYSound.as, Line 106 1120: Access of undefined property AIR.
    L:\Flash\Probes_works\Probe\claus-as3icy-6db44ab\source\com\codeazur\as3icy\ICYSound.as, Line 198 1120: Access of undefined property AIR.
    L:\Flash\Probes_works\Probe\claus-as3icy-6db44ab\source\com\codeazur\as3icy\ICYSound.as, Line 240 1120: Access of undefined property AIR.
    L:\Flash\Probes_works\Probe\claus-as3icy-6db44ab\source\com\codeazur\as3icy\ICYSound.as, Line 441 1120: Access of undefined property AIR.
    Do you have an ideas?

  7. Alex, go to Advanced ActionScript 3.0 Settings, “Config constants” tab, add “CONFIG::AIR” with a value of “true” or “false”.

  8. Hi!
    Another problem: than i “test movie” in Adobe Flash CS5.5 everything works perfectly. But than i launch svf in explorer- nothing. Help me.

  9. Hi, good work for this classes. I have a little problem with your test in AIR, when i play a station For example: “http://188.241.154.63:9000/;”, start correctly and listen the music, and show me the current Song name. But a few minutes the app show me strange characters and (e.metaData.length > 0) always true. Then crash
    and show this:

    Error: Error #2030: End of file was encountered.
    at flash.utils::ByteArray/readUnsignedByte()
    at com.codeazur.utils::BitArray/readBits()[C:\Users\Jose\Desktop\ShoutCast Player\as3icy-master\as3icy-master\source\com\codeazur\utils\BitArray.as:21]
    at com.codeazur.as3icy.decoder::LayerIIIDecoder/decodeFrame()[C:\Users\Jose\Desktop\ShoutCast Player\as3icy-master\as3icy-master\source\com\codeazur\as3icy\decoder\LayerIIIDecoder.as:108]
    at com.codeazur.as3icy.decoder::Decoder/decodeFrame()[C:\Users\Jose\Desktop\ShoutCast Player\as3icy-master\as3icy-master\source\com\codeazur\as3icy\decoder\Decoder.as:22]
    at com.codeazur.as3icy::ICYSound/readFrame()[C:\Users\Jose\Desktop\ShoutCast Player\as3icy-master\as3icy-master\source\com\codeazur\as3icy\ICYSound.as:371]
    at com.codeazur.as3icy::ICYSound/readLoop()[C:\Users\Jose\Desktop\ShoutCast Player\as3icy-master\as3icy-master\source\com\codeazur\as3icy\ICYSound.as:232]
    at com.codeazur.as3icy::ICYSound/progressHandler()[C:\Users\Jose\Desktop\ShoutCast Player\as3icy-master\as3icy-master\source\com\codeazur\as3icy\ICYSound.as:226]

    Please help me. Best regards,
    JMiguel

Comments are closed.