HDHomeRun

HDHomeRun is a ATSC/QAM Digital TV Tuner with Ethernet Interface. This device has two different models, one is a single tuner and the other a dual tuner, but the functionality is the same. The is control software available for Windows, Mac and Linux and best of all the software is open source allowing you to embed it in your own solution. This is the easiest way I know of to take the digital video signals from over the air or cable and get them onto your IP network where you can do interesting things with them.

Now that you have your HDHomeRun and established IP freedom for your digital video, what are you going to do next? One easy way to enjoy the newfound freedom is to grab a copy of VLC and your hdhomerun_config utility and start watching you favorite shows. So get your network setup and have at it.

Encoding

OK, well that’s fun but what you really want is to record your shows and watch them whenever (and wherever) you want. Hmm, this sounds like a good fit for a highly concurrent, fast IO, superb networking language that doesnt quit unless you nuke it from orbit because let’s face it the last season of Lost is really that important. I’m of course talking about Erlang, which is a programming language that was designed at the Ericsson Computer Science Laboratory. Erlang is a functional programming language which is ideal for the development of highly available soft realtime systems.

The output from the HDHomeRun is a single or multiprogram transport stream depending on if the program number is set, that is sent via unicast UDP to the destination specified in the target destination to the tuner. The Video and Audio codecs that are used are dependent on the broadcast source, for ATSC it’s MPEG-2 Video and AC3 (A52) Audio. I’m not going to get into transcoding, so we will just capture the broadcast as is to disk.

For Erlang this is simply spawning a process that opens a udp socket, opens the output file then goes into a loop receiving packets on the socket and writing them out to disk. There is a little more complexity around the timing between the control commands to the HDHomeRun and the listener process, basically something has to be listening on the destination socket before HDHomeRun will start sending packets. The other complexity is that Erlang has a robust process supervisor model, so that if things like your listener process dies it can be restarted, but lets say you put a timer into the listener process that stops the process if it does not receive data for 1000 milliseconds. If the listener process exits because of a timeout, then you probably want to restart the listener then immediately retune the HDHomeRun to get the data flowing again. I’m not going to address the supervisor model either, since my focus is on the control commands to HDHomeRun.

We could issue the control commands from Erlang by calling external scripts or creating a port process, but Erlang has very nice bit syntax and binary matching that make it ideal for network programming. As mentioned above the C code that is available for download provides the complete implementation of the control program, but one of the files hdhomerun_pkt.h covers some details of the actual network protocol. The packet format is described below the first two bytes are the Packet type, the next two are the payload length, then the payload followed by a 32 bit CRC of the entire packet.

/*
  The discover protocol (UDP port 65001) and control protocol (TCP port 65001)
  both use the same packet based format:
   uint16_t    Packet type
   uint16_t    Payload length (bytes)
   uint8_t[]   Payload data (0-n bytes).
   uint32_t    CRC (Ethernet style 32-bit CRC)
*/

The Packet type is one of the following six: Discover Request, Discover Reply, Get/Set Request, Get/Set Reply, Upgrade Request or Upgrade Reply. The Upgrade Request and Reply are used to upgrade the firmware on the HDHomeRun box, since this is a sensitive operation and does not occur during normal operation, I will not try to replicate that with the Erlang code.

#define HDHOMERUN_TYPE_DISCOVER_REQ 0x0002
#define HDHOMERUN_TYPE_DISCOVER_RPY 0x0003
#define HDHOMERUN_TYPE_GETSET_REQ 0x0004
#define HDHOMERUN_TYPE_GETSET_RPY 0x0005
#define HDHOMERUN_TYPE_UPGRADE_REQ 0x0006
#define HDHOMERUN_TYPE_UPGRADE_RPY 0x0007

To be continued…