Broccoli Products home | contact us | about us
BBC Radio Downloader Project in C#
(by Lou Burne, last updated 9-DEC-2011)
The Brief
This is a guide for C# developers who want to create an iRecorder - an application that downloads radio programmes from the BBC iPlayer website. This sample project downloads the latest episode of The Archers.  I will start by describing how to jump the technical hurdles, and finish with a description of the functions in the sample code provided.

The code for this project can be found in Program.cs, a C# file that compiles as a console application.  You will need some support files (radio_files.zip) and the latest RTMP Client library.

The function names and code shown on this page are taken from the Program.cs file. This project was developed with VS2010, .Net4 and Windows 7.
Support File: radio_files.zip
Sample Program: Program.cs
Last Updated: 1-FEB-2011
System Requirements:    Windows 7
An understanding of HTTP protocols, socket communications and XML is required. However these functions are neatly encapsulated by the Microsoft .NET libraries and support files, and I have commented the code to make it clear what is going on.

Throughout this guide the word "programme" refers to a radio programme, such as "Woman's Hour". I will use the term "application" to refer to a compiled executable.

What is our brief? To locate a media stream for the most recent episode of The Archers from BBC's iPlayer website, download the streamed episode, and store it in a file (the "target file") in a convertible format (FLV).
Recent Changes to BBC Streaming
An application was recently available from this page that downloaded MP3 files from the iPlayer website by pretending to be an iPhone.  This wont work any more.  The BBC has implemented the security certificate protocol that requires a client certificate to be available on the iPhone before programmes can be downloaded.  Downloading MP3 files and video MOV files directly from the BBC's website is no longer possible.  However the alternative method of RTMP streaming is.
Obstacles
First Obstacle The programme is streamed by RTMP, but where is it?  Given a programme title how do we obtain an RTMP server address?

Second Obstacle What do we use to download the RTMP stream?  Fortunately an RTMP Client class library is available from this website, and this library can be used with a C# application.  Solved!

Third Obstacle If we saved all of the data from an RTMP stream to a file, what would play that file?  What is streamed to us is the guts of the file - we need to add formatting and a header to compose a valid FLV file from the streamed data.

Fourth Obstacle How do we convert the FLV file into an MP3, or whatever format we want?

Fifth Obstacle Because audio streams are intended to be played live, there is no point an RTMP server streaming data at maximum rate, because the software playing that data will not play it any faster.  But we are not playing the stream, we are downloading it, so we will want the data as fast as possible.
Where Is The Stream Of Audio Data?
There are many hoops to jump through going from a programme title (in our sample "The Archers") to a media stream location.  I have wrapped all of this up into the BBCHelper.cs support file (see radio_files.zip), and what goes on in there is as follows:

Bullet Find all of the programmes that begin with the string "The Archers".
Bullet For the first programme found, find all of the episodes for this programme.
Bullet From this list of episodes, choose the most recent one.
Bullet For the chosen episode, download a list of available media streams.  There is often more than one stream available, ranging from 128 to 48 bitrate.
Bullet Pick one of the media streams that is in AAC format and streamed by RTMP.
Downloading RTMP Stream
Once the location of an RTMP stream is known, we can use the RTMP client to download the stream over a socket connection.

In the sample application  Program.cs a callback function is declared, and this is called whenever an RTMP message has been received.  When the callback function receives an audio message, the payload of this message is a single AAC packet, and can be stored in the FLV file.
FLV File Format
The FLV file format is a skeleton format - an FLV file can contain audio and video encoded in many different formats. Internally, an FLV file consists of a file header followed by a series of chunks of data, called tags.  Tags consist of a header and payload of data.  There are three types of tag - audio, video and script. 

The first tag in an Flv file must be a script tag containing metadata describing the file content.  The proceeding tags are audio and video tags.

The format for the data in a script tag is the same Amf0 format used in RTMP, so we can pass the parameters in our RTMP message directly to the Flv file to be saved as a script tag.

A full specification for the FLV file format can be found here.

The class BroccoliProducts.FlvFile, included in the RtmpClient library, manages the stuffing of a newly created FLV file with RTMP audio packets.  For our iRecorder we are expecting audio in AAC format, but since we pass the streamed metadata directly into the Flv file, we don't need to know what format it is in - we just pass packets from the RtmpClient to the Flv file.

The process of converting the RTMP audio data into a valid FLV file using the FlvFile class is as follows:

1 Call the following static function to create a new FLV file:

FLVFile CreateFileW( string strFilepath )

The new FLV file will be completely empty.
2 When the first RTMP metadata packet is received, pass the parameters to the FlvFile object.  This creates the Flv file header, and adds the first script tag.

AddScriptTag( UInt32 uiTimestamp, UInt32 uiStreamId, RtmpParameterList paramList );
3 For each RTMP audio packet received, add this to the file using the function:

void AddAudio( UInt32 uiTimestamp, UInt32 uiStreamId, byte[] buffer, int iBufferLength)
4 When streaming has finished, update the metadata/script tags with the duration of the file:

SetScriptParameter( string strLabel, double dblewValue );
3 Finally, call Dispose on the FlvFile object to close the underlying file stream.
What To Do With An Flv File
Flv files are usually played in an Adobe Flash player embedded in a web page.

VLC Media Player
To play an Flv without using a browser try VideoLan's VLC Media Player, which is open-source, and plays a huge range of formats, both audio and video.

Flv is a skeleton file format, and finding a single free application to convert an Flv file to another format is not easy. 

There are two freely available applications, Mplayer and Lame.

Mplayer will convert an FLV file into a WAV audio file or AVI video file.

Lame is a freely available open-source application for converting WAV files into MP3.

Mplayer and Lame are both console applications, so can be run from a batch file or by starting a process in code.

The batch file for converting an FLV called hello.flv into an MP3 will look like this:

Mplayer hello.flv -ao pcm:file=output1.wav

Lame --priority 0 -b 128 -h --resample 44.1 output1.wav output1.mp3

Speeding Up Stream Downloads

As mentioned above, RTMP server do not always stream at their maximum rate.  If the stream is intended to be played live, the stream only needs to arrive just short of real time.  The BBC's servers do this.  About every 10 seconds the streaming pauses for about 9 seconds.  So downloading a 30 minute file can take about 30 minutes.

However, the RTMP server can be fooled into cutting short the pauses if our RtmpClient also pauses the stream, and then immediately unpauses it.  In the code this is done in the callback function, sections D and E.

When a control message is received indicating that the stream buffer is empty, we know the RTMP server is about to pause.  So we send a pause command to the server.

When a command message is received notifying us that the pause request has been processed and the stream is now paused, we unpause the stream.

The result is a consistently fast download rate.
Application Flow
Now we know all we need to know, lets plot an application flow. Below is a table of the application functions in the same order they appear in the example code Program.cs.

Main Entry Function (Main)

A Read the application command-line parameters ("arguments"). There is only one - the target directory.

The default target directory is My Documents.

To store the programme somewhere else, specify in the form "-targetfolder=<path>". This folder must exist.
_readApplicationArguments
B Get a media stream location for the most recently available episode of "The Archers".
_getEpisodeDetails
C Combine the programme and episode titles to form a unique and helpful target filename.
_makeUniqueFilepath
D Create an FLV file.  Opens an RTMP client connection, connects to the RTMP server, and saves the incoming audio data to the FLV file. _rtmpDownload


RTMP Download (_rtmpDownload)
A Create a new FlvFile object.
B Declare an RtmpClient object.

Set EventsEnabled to false since we will not be using StatusChange events.
C Declare the connection and stream objects we will be using for the streaming.
D Create a connection to the BBC server, setup the connection parameters, and connect to the RTMP application.
E Using our connection, create a new stream object.
F Construct a WorkingSet object.  The WorkingSet class wraps multiple object references into a single object we can pass to the callback function.
G Register our callback function, use the workingSet object as out callback parameter, and identify which message types we want to be called back on.
H Start playing the stream.
I Loop until the stream is complete, or the user presses the "C" key.
J Clean up.  Close the stream, then the connection.  Before closing the Flv file, update the duration in the script tags with the time4stamp of the last received audio tag.

The Callback Function (fnCallback)
A Cast the callback parameter to our WorkingSet object.
B For incoming audio data, check that the metadata header has been written to the Flv file, and then add the audio data to the Flv file.

Track the most recent timestamp for audio data, as this provides a measure of the duration of the file.
C For incoming metadata, if this is the header for the Flv file, add the parameters to the Flv file as a script tag.
D If a command message is received, and the stream has been paused, unpause the stream.
E If a control message is received informing us that the stream buffer is empty, pause the stream.
Contact form 
Use the contact form to send comments and requests for information to Broccoli Products.
Topic:
Message:
Email:
More Information
Broccoli Products Ltd © 1998-2012 Broccoli Products Ltd
Reg Number: 2895355
Reg Office: 27 Old Gloucester Street, London. WC1N 3AX
Privacy Policy
Copyright Notice
Liability Disclaimer
Contact Us