Icecast Interface

This module implements the interface to Icecast.

The stream uses the musicdb.lib.stream.libshout2 module as wrapper to libshout from the Icecast project.

Introduction to Icecast

A setup using Icecast consist of three components. Using Icecast’s terms, the are the following:

Source Client:

This the software that provides the music. In this setup, it is MusicDB. It is called client, because it connects to Icecast that can manage multiple source clients.

Icecast Server:

This is Icecast itself. It servers the audio stream to the world.

Listener:

The listener is the receiver of the stream. For example VLC.

Icecast can handle multiple sources. Each source is used for the input of a Mountpoint. All Listeners that want to access the audio stream receives the data from this Mountpoint’s output.

For more details see the Icecast Documentation.

Icecast Configuration

The Icecast configuration defines the Source Client and how the data from the Source Clients gets served to the listeners.

In this section, I only point out the more important settings of the whole Icecast configuration. There is a good example configuration provided by MusicDB. The following list addresses the settings in the XML file for the Icecast configuration.

icecast/authentication/source-password:

This is the password MusicDB needs to know to connect to Icecast. So this password must be the same as in MusicDB Configuration [Icecast]->password.

There are two ports needed for the MusicDB/Icecast setup. One port to connect MusicDB to Icecast, and one to provide the stream to Listeners. The MusicDB connection (Port 8001) is bound to localhost and not encrypted. The other port (8000) can be open to the world because it will be encrypted and protected against unwanted access.

The ports will be configured in the icecast section of the configuration file by adding listen-socket sections:

<!-- Extern -->
<listen-socket>
    <port>8000</port>
    <ssl>1</ssl>
</listen-socket>

<!-- Intern -->
<listen-socket>
    <port>8001</port>
    <ssl>0</ssl>
    <shoutcast-mount>/stream</shoutcast-mount>
</listen-socket>
icecast/listen-socket/shoutcast-mount:

This setting defines the Mountpoint name (starting with “/”). It must be the same like the one set in MusicDB’s configuration: [Icecast]->mountname Further more it must be equal to the name defined in the detailed mount specification: icecast/mount/mount-name.

Icecast Class

class musicdb.lib.stream.icecast.IcecastInterface(port, user, password, mountname)[source]

This Icecast interface manages the connection to the Icecast server.

The following values for setting up the connection via libshout are hard coded. They can easily be changed in the Icecast configuration in the icecast/mount section.

Parameter

Value

Comment

protocol

SHOUT_PROTOCOL_HTTP

The native and recommended format for Icecast 2

format

SHOUT_FORMAT_MP3

Obvious. MusicDB streams mp3 files

public

0

For security and privacy reasons

host

"localhost"

For security reasons MusicDB and Icecast must run on the same computer

name

"MusicDB Stream"

dumpfile

None

Storing the stream does not make sense for non-moderated streams

url

None

There is no website for the stream, because it is a private stream

genre

None

Not relevant for private streams

description

None

Not relevant for private streams

agent

None

Not relevant

audio_info

None

Not relevant

The Icecast Interface class can stream any data to the Icecast Server via StreamChunk(). In general this should be not necessary! Instead only use the StreamFile() method that streams a mp3 file addressed by its path. Advantage of the StreamFile() is, beside a clean frame wise transfer of the data to the server, that the stream can be muted via Mute().

Parameters
  • port (int) – port number for the Source Client connection

  • user (str) – Name of the source user

  • password (str) – The password of the source user

  • mountname (str) – Name of the mountpoint to use.

Example

# Create IcecastInterface object
icecast = IcecastInterface(666, "source", "hackme", "/stream")

# Connect to the Icecast server
icecast.Connect()

# Stream one file
for size, offset in icecast.StreamFile("/tmp/test.mp3"):
    print("%i of %i bytes sent"%(offset, size))

# Disconnect from the Icecast server
icecast.Disconnect()
Connect()[source]

Tries to connect to Icecast.

If already connected, only True gets returned with out opening again.

Returns

True on success, otherwise False

Disconnect()[source]

Tries to close the Icecast connection.

If already disconnected, only True gets returned with out disconnecting again.

Returns

True on success, otherwise False

IsConnected()[source]
Returns

True if connected to Icecase. False of not connected.

Mute(state=True)[source]

This method sets the Icecast Interface into a mute state (when state == True). Then, the method StreamFile() streams silence instead of the file content. After leaving the mute state, the file streaming continues at the position it got muted (=paused)

Parameters

state (bool) – Mute stream when True, otherwise continue streaming audio

Returns

Nothing

StreamChunk(chunk)[source]

This method send a chunk of a file to the Icecast server. The method synchronizes with Icecast before sending the data. This is a blocking process!

When sending a chunk of data to Icecast fails, the method disconnects from the Server.

Parameters

chunk (bytes) – A chunk of a file to stream to Icecast

Returns

True on success, False otherwise

Raises

TypeError – When chunk is not of type bytes

Example

with open(path) as mp3:
    while True:
        chunk = mp3.read(4096)
        if not chunk:
            print("File sent.")
            break

        retval = icecast.StreamChunk(chunk)
        if retval == False:
            print("ERROR")
            break
StreamFile(path)[source]

This is a generator that sends a mp3 file to the Icecast server. The mp3 file gets split into its frames, and sent frame wise.

After sending one chunk, the generator returns a dictionary with exact the keys and values that gets returned by musicdb.lib.stream.mp3stream.MP3Stream.Frames() and musicdb.lib.stream.mp3stream.MP3Stream.AnalyzeHeader()

This frame dictionary gets extended by one further key: muted. When this value is True, then a silent frame got sent. In this case, all information in the dictionary relate to the next frame from the mp3 file that will be send when the stream continues. When False then the information are related to the actual sent frame.

Streaming of the file using this method allows to pause the audio stream by calling the Mute() method. Then instead of the frames from the file, a hard coded frame of pure silence gets streamed as long as the mute-state persists. Instead of one silent frame, 10 frames will be streamed at once. This is about 261ms of silence.

The control flow is visualized in the following image:

digraph hierarchy { size="5,8" start [label="Start"]; loadmp3 [shape=box, label="Load mp3 file"] getframe [shape=box, label="Get next frame"] ismuted [shape=diamond, label="state == muted ?"] streamsilence [shape=box, label="Stream silence"] yieldnextframe [shape=box, label="Return frame"] streamframe [shape=box, label="Stream frame"] yieldframe [shape=box, label="Return frame"] end [label="Stop generator"]; start -> loadmp3 loadmp3 -> getframe getframe -> ismuted [label="Frame available"] ismuted -> streamsilence [label="Yes"] ismuted -> streamframe [label="No"] streamsilence -> yieldnextframe streamframe -> yieldframe yieldnextframe -> ismuted yieldframe -> getframe getframe -> end [label="No further frames"] streamsilence -> end [label="On error"] streamframe -> end [label="On error"] }

The silent frame is optimized to be injected in the mp3-files generated by MusicDB for the MP3 Cache. The mp3 file was generated as shown in the script example below. Then the first frame was captured via the musicdb.lib.stream.mp3stream.MP3Stream.Frames() method.

ffmpeg -filter_complex aevalsrc=0 -acodec libmp3lame -ab 320k -t 1 monosilence.mp3
ffmpeg -i monosilence.mp3 -ab 320k -ac 2 stereosilence.mp3
Parameters

path (str) – Absolute path to the mp3 file to stream. The encoding must be the same for all files!

Returns

Returns a generator that returns the currently streamed frame.

Example

for frameinfo in icecast.StreamFile(path):
    if frameinfo["muted"] == True:
        print("Stream is muted.")
        continue

    print("%i. frame of %i frames sent. Length: %s ms"%(
            frameinfo["count"],
            frameinfo["total"],
            frameinfo["header"]["frametime"]
            ))
UpdateTitle(title)[source]

This method updates the stream title.

Parameters

title (str) – A string as new title for the stream - for example the song name

Returns

True on success, False otherwise

Raises

TypeError – When title is not of type string