How to send audio stream to an Icecast server in Python

147 Views Asked by At

I am having some issues trying to properly send audio data from a file to an Icecast server in Python.

Here is my class :

import requests
from base64 import b64encode

class IcecastClient:
    def __init__(self, host, port, mount, user, password, audio_info):
        self.host = host
        self.port = port
        self.mount = mount
        self.user = user
        self.password = password
        self.audio_info = audio_info  # Additional audio information
        self.stream_url = f"http://{host}:{port}{mount}"

    def connect(self):
        # Basic Auth Header
        auth_header = b64encode(f"{self.user}:{self.password}".encode()).decode("ascii")
        self.headers = {
            'Authorization': f'Basic {auth_header}',
            'Content-Type': 'audio/mpeg',
            'Ice-Public': '1',
            'Ice-Name': 'Auralyra Stream',
            'Ice-Description': 'Streaming with Auralyra',
            'Ice-Genre': 'Various',
            'Ice-Audio-Info': self.audio_info
        }

    def stream_audio_file(self, file_path, chunk_size=4096):
        with requests.Session() as session:
            session.headers = self.headers

            with open(file_path, 'rb') as audio_file:
                while True:
                    chunk = audio_file.read(chunk_size)
                    if not chunk:
                        break  # End of file

                    try:
                        response = session.put(self.stream_url, data=chunk)
                        if response.status_code != 200:
                            print(f"Streaming failed: {response.status_code} - {response.reason}")
                            break
                    except requests.RequestException as e:
                        print(f"Error while sending audio chunk: {e}")
                        break

                if response.status_code == 200:
                    print("Streaming successful")

    def send_audio(self, audio_chunk):
        try:
            # Send the chunk using the session with predefined headers
            response = self.session.put(self.stream_url, data=audio_chunk)
            if response.status_code != 200:
                print(f"Streaming failed: {response.status_code} - {response.reason}")
        except Exception as e:
            print(f"Error while sending audio chunk: {e}")

The problem is that although Icecast recognizes the stream and the status of the mountpoint looks good, trying to listen to the stream doesnt work at all. My guess is that this as to do with how I send the data to the server.

PS: I am trying to avoid using librairies like 'shout' to do that

1

There are 1 best solutions below

0
Brad On

You can't just send the audio to the server at an arbitrary speed. You must send it at the speed in which it would be played back.

The server doesn't really know or care about the timing information in the stream. It simply buffers a bit, and when clients connect it reads from the buffer and sends them fresh data as it comes in. So, if you shove an hour's worth of audio through the server as fast as it will eat, there will be only a few seconds of buffer available to clients when they connect.

I know you say you don't want to use other libraries for this, but you probably should. You actually need to make a continuous stream and not just send file after file. This means all your assorted audio files to PCM, "playing out" that PCM at the correct rate to the codec, and sending the encoded data to the server as it comes from the codec. This is quite a bit to reinvent. At the very least, you'll have to send your pre-encoded files at the right rate, and those files would have to be free of ID3 tags, and would need to be the same sample rate and channel format.