Adventures, life experiences, programming.

Building a real time streamable online FM radio player for fun!

I always wanted to play around with how real-time streaming protocol works and I did dive into modern protocols like HLS, RTMP, DASH/MPEG-DASH, and HDS but I kept forgetting them because I did not have enough fun implementing them or building something cool and easy to use. I had this idea to build an app where I could stream FM radio from anywhere around the world. In other words, if I were in Australia or the United States, I want an application to get a live stream of a local FM broadcast anywhere in Asia( a glimpse of the idea shown below). Sounds interesting right?




Before we dive into the solution or discuss what I achieved to make the idea work, I would like to state one thing re-transmitting the radio frequency should adhere to the local laws of the country you are residing in and should be done with prior approval from the official telecommunication government. Sounds scary, probably yes because we do not want to end up in prison for doing something against the local laws.
Before we jump into the solution, we have a basic idea what we want to achieve. We have a local FM radio station thats emitting a radio signal, we want to be able to simulate a fm radio player online that could play streams and be accessible anywhere around the world. The bad news is that fm signals that are emitted are analog and our digital computer/laptop has no way to know or catch these signals. To have that signals captured, we require some device called SDR(Software Defined Radio) Receivers which allows as a software interface to be able to intercept and write applications that can decode these data and meaning out of it. SDRs are readily available in the market, one from amazon which you might find it here.




Above is the a rough idea of the pieces to use to make this idea work. Remember this does not address high scalability, fault tolerance(hardware+software), monitoring, latency, etc. If this is something that is fun and interesting to you, let’s have a discussion in the comments or feel free to drop an email. Let’s desribe the componets in details

Components:

  1. Local Radio Station: Station emitting your radio signals.
  2. Linux machine/Host Machine: the host where you want to capture the signals, decode them, encode them in the right format and sink it in some streamable format.
  3. FFMPEG: FFmpeg is a comprehensive, open-source software project that produces libraries and programs for handling multimedia data. It’s widely used for a variety of tasks related to audio and video processing. Key aspects of FFmpeg include:
  4. RTL-SDR: refers to the use of certain Realtek DVB-T (Digital Video Broadcasting — Terrestrial) receivers as software-defined radios (SDRs). Originally designed for television reception and streaming, these inexpensive USB devices were found to be capable of receiving a wide range of radio frequencies, making them useful for a variety of radio hobbyist and professional applications. Here are the key points about RTL-SDR:
  5. IceCast:
    Icecast is an open-source streaming media server that allows for the digital broadcasting of multimedia content over the internet. It’s particularly popular for streaming audio content, like radio stations or podcasts. Here are some key points about Icecast:

    1. Versatility: Icecast can stream both live audio and video content, although it’s most commonly used for audio. It supports various media formats, including Ogg (Vorbis and Theora), Opus, WebM, and MP3.
    2. Source Client and Server Model: Icecast operates on a client-server model. The server runs continuously, waiting for source clients to connect and send a stream, which it then broadcasts to listeners. Source clients, like IceS or Liquidsoap, encode and send the audio data to the Icecast server.
    3. Multiple Mount Points: One of Icecast’s distinctive features is its ability to handle multiple streams (or “mount points”) simultaneously. This means a single Icecast server can broadcast several different streams, each on its own mount point, allowing for multiple channels or stations
    4. Format Agnostic: Icecast is not tied to any specific media format. It can distribute any kind of audio or video stream as long as the client can support the encoding
    5. Cross-Platform: It runs on various operating systems, including Linux, macOS, and Windows
    6. Listeners: People can listen to the Icecast streams through various media players that support streaming audio, such as VLC, Winamp, or through web-based players
    7. Use in Internet Radio: Icecast is particularly popular in the internet radio community, as it’s a robust, scalable, and flexible solution for broadcasting audio streams over the internet.


    The idea is quite simple.

    1. You catch the radio signals emitted by the station for a particular frequency via the SDR and emit raw UDP streams.
    2. You use something like ffmpeg or vlc to catch this raw UDP streams and encode it in a format that’s playable on the internet.
    3. You distribute the stream(buffered) to a local source which then can be distributed over a CDN (via HLS) or ICECAST as used here.
    4. you exposed a user interface exposed deployed somewhere for users to connect to your stream.

Now you have that device, you could plug this device into your USB port and start capturing signals from a software tools here. Then you can start a udp stream something like

rtl_fm -f 100M -M wbfm -s 200k -r 48k -

This command tunes to 100 MHz (-f 100M), sets wideband FM demodulation (-M wbfm), sample rate (-s 200k), and resamples the audio to 48 kHz (-r 48k). The - at the end signifies that the output will be piped to another command.

We can pipe this to ffmpeg (you could use something like netcat or socat to check if you are indeed receiving stream or even VLC to play raw udp stream from the player). For now, we will focus on ffmpeg

ffmpeg -f s16le -ar 48000 -ac 2 -i udp://localhost:8223 -c:a libmp3lame -b:a 128k -ar 44100 -content_type audio/mpeg -f mp3 -muxdelay 0.1 icecast://source:password@icecast_server:port/mountpoint


let’s undestand what this command does:

This command uses FFmpeg, a powerful multimedia framework, to capture audio from a UDP stream and then encode and stream it to an Icecast server. Let’s break it down:

  1. ffmpeg: This initiates the FFmpeg program, a versatile tool for handling audio, video, and other multimedia formats and streams.
  2. -f s16le: This option sets the input file format. s16le stands for signed 16-bit little-endian samples. This is a raw audio format with no header.
  3. -ar 48000: This sets the audio sample rate for the input file to 48,000 Hz, which is a common sample rate for high-quality audio.
  4. -ac 2: This specifies the number of audio channels in the input file. 2 indicates stereo audio.
  5. -i udp://localhost:8223: This specifies the input source. -i stands for input, and udp://localhost:8223 indicates that the input is being streamed over UDP (User Datagram Protocol) on the local machine at port 8223.
  6. -c:a libmp3lame: This sets the audio codec for the output file. libmp3lame is an MP3 encoder that provides good quality and compression.
  7. -b:a 128k: This sets the audio bitrate for the output file to 128 kbps, which is a balance between quality and file size for MP3 audio.
  8. -ar 44100: This sets the audio sample rate for the output file to 44,100 Hz, the standard for MP3 audio.
  9. -content_type audio/mpeg: This specifies the MIME type of the streaming content as audio/MPEG, which is suitable for MP3 streams.
  10. -f mp3: This sets the format of the output file to MP3.
  11. -muxdelay 0.1: This sets the maximum delay for multiplexing (combining streams) to 0.1 seconds. It can help in synchronizing audio and video streams, although in this case, it’s just for audio.
  12. icecast://source:password@icecast_server:port/mountpoint: This is the output URL. It tells FFmpeg to send the encoded stream to an Icecast server. The format includes the username (source), password, server address, port number, and the specific mount point on the server where the stream will be sent.

Combining commands and piping the output from one to the other yields us something like this:

rtl_fm -f 100M -M wbfm -s 200k -r 48k - | ffmpeg -f s16le -ar 48000 -ac 2 -i udp://localhost:8223 -c:a libmp3lame -b:a 128k -ar 44100 -content_type audio/mpeg -f mp3 -muxdelay 0.1 icecast://source:password@icecast_server:port/mountpoint


Overall, this command captures raw audio from a local UDP stream, encodes it to MP3 format using specific quality settings, and then streams it to an Icecast server for broadcasting.

If you are confused about what icecast, lets abstract as as a stream server which receives encoded stream from the ffmpeg. If you aren’t familiar with what piping and how it works, here’s an article for you.

Before you start this, you start ffmpeg stream, you must have a IceCast server ready with the listeners and mountpoints configured and ensuring the firewalls allow incoming connections to write to the mountpoint. Confused about mountpoint? Here’s a short explanation.

In Icecast, a “mount point” is a concept used to distinguish between different streams being broadcast from the same Icecast server. Here’s a detailed explanation:

  1. Stream Identification: A mount point is essentially a unique identifier or URL path used to separate different audio or video streams on an Icecast server. Each mount point represents a different stream.
  2. Example: If your Icecast server’s URL is http://example.com, and you have two streams for different music genres, you might set up two mount points: /rock for a rock music stream and /jazz for a jazz music stream. The URLs for these streams would then be http://example.com/rock and http://example.com/jazz.
  3. Multiple Streams on One Server: The concept of mount points allows a single Icecast server to broadcast multiple, distinct streams simultaneously. This is particularly useful for broadcasters who want to offer a variety of content types or channels.
  4. Configuration: In the Icecast configuration file (icecast.xml), each mount point can be configured with specific settings like maximum listener count, fallback mount point, authentication details, and metadata settings.

    Now you have all the pieces connected, you require a simple html page that connects to the icecast server and lets users stream audio from the icecast mount servers.

    Here’s a simple html page that does the job:
<!DOCTYPE html>
<html>
<head>
    <title>Icecast Audio Stream</title>
</head>
<body>
    <h1>Icecast Audio Stream</h1>

    <audio controls>
        <source src="http://icecast_server:port/mountpoint" type="audio/mpeg">
        Your browser does not support the audio element.
    </audio>
</body>
</html>

That’s it, if you have followed the instructions so far, you will be able to deploy a streamable FM player for users anywhere around the world to listen to your local stream:



Before we end, engineering is always exciting. Isn’t it?
1. How can this solution be scaled to millions of users?
2. How would you design your solution to be fault tolerant(hardware+software)?
3. How would you handle machine failures and power failures?
4. What would you change in the design to tolerate minimal latency?
5. How would you use CDN or other mechanisms to bring content closer to users?
6. How would you deploy monitoring solutions to ensure users have access to stream 24/7

If you have thoughts about this or would love to strike a conversation, feel free to drop me an email at [email protected] or start a conversation in the comments below.

Leave a Reply

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