HTTP live streaming a radio scanner

2009-07-13

As my obsession with everything radio related continues, I found myself wanting a way to listen to arbitrary radio frequencies from my iPhone. I looked at several solutions along these lines, but settled for using an off-the-shelf radio scanner as a frontend due to the low cost, relative ease of use, and wideband reception. My portable scanner, an ICOM IC-RX7 certainly has all of the features necessary to accomplish this task, but is limited in that it's small package makes it difficult to setup a discriminator tap. A discriminator tap is a radio modification that allows you to capture the baseband radio signal before any real processing is done by the audio frontend. This is useful if you want to decode FSK encoded signals for instance.

So, rather than putting holes in my pretty portable radio, I opted to buy a new one. I figured this would be a good time to fill in for a few of the features the RX7 is lacking, like trunk tracking. After a bit of research and deciding where my price points were, I ended up with the Uniden BCT8 scanner. It supports a few common analog voice trunking systems (EDACS, LTR, Motorola) and has a serial port for controlling and programming.

As my platform of choice is Linux, I started searching for software to interface with the radio over the serial port and came up short. However, I did manage to find Uniden's serial protocol reference for the BCT8 and proceeded to implement a few Python modules to serve my purposes. As a side note: Uniden apparently cannot design a sane protocol and their engineers should be shot on sight.

With the eventual goal of getting scanner audio and control on my iPhone, I began to tackle the task of taking audio input from ALSA and pushing it over the network in some format that the iPhone can comprehend. My initial solution was to use the quite convenient (if somewhat undocumented) pyAudio library to capture raw PCM data from the sound card and multicast it out over my network. This worked fairly well for listening using my laptop, but would require some serious development work to get working on the iPhone (A custom Audio Unit Generator using multicast sockets to recieve the stream and somehow transforming it into something Core Audio can comprehend). Even if I did get that working, there would be limitations... It wouldn't work on a network without multicast, audio would be transmitted uncompressed, and the possibility of high-latency or dropped packets was high.

As I was working on the multicast server, Apple announced a new HTTP Live Streaming feature in the iPhone 3.0 firmware. A quick look at the documentation make it look fairly simple, as long as I could get my PCM audio stream into AAC or MP3 in an MPEG2 Transport Stream. The encoding part had proven more difficult than I thought and wouldn't work with straight UNIX pipes. I was also unable to find a good library for muxing MPEG2 Transport Streams... I figured that in the worst case I could use a Python module I wrote a few months ago for demuxing MPEG2-TS, but that would require quite a bit of work and may be filled with bugs.

Eventually I found gstreamer, which solves a lot of the above problems. I built a gstreamer pipeline that took input from alsasrc, compressed it using ffenc_libmp3lame, muxed using ffmux_mpegts, and output using tcpclientsink. The tcpclientsink is then connected to a Python app that reads input from a source socket and outputs the data as a chunked HTTP response to any open connections. I used WSGI to implement a simple server that handles the protocol portion of Apple's HTTP live streaming spec. In order to handle multiple clients without too much hassle, the streaming server uses flup to implement a FastCGI server that's frontended by nginx.

After bringing everything up and connecting the iPhone, I get a nice audio stream on the iPhone. There are still a few drawbacks to this approach, the most obvious being that I can't control the scanner from the phone. I'm hoping that Apple provides a way to integrate a live streaming connection directly into an app without popping up the QuickTime view, but I may run out of luck there. Worst case, I can provide some status information about the scanner (currently tuned frequency, talkgroup, etc) as a video stream in the transport stream and a separate web app for performing control actions.

I'll go into more detail about the implementation of the various pieces here in another post, but in the meantime the scanner's stream is available here: http://neohippie.net/scanner/ and is accessible using an iPhone with 3.0 firmware or VLC. Theoretically it should also work with QuickTime X, but that hasn't been released yet so I haven't been able to test it.

Next post - OpenSSH key visualization