Richard Ive
Richard Ive

Reputation: 3

Stream mp3 icecast data using python

I'm trying to write my own socket streamer where I connect to a know mp3 source using python's socket, stream the data and try and pass it into alsaaudio as pcm data.

I know have to get the icy-metaint, read that many bytes, get the first byte for the metadata length then continue reading metaint bytes.

Unfortunately I've run out of knowlege and the code below results in white noise being played.

Any help would be amazing. Thank you!

#!/usr/bin/env python
import socket
import sys
import alsaaudio
import time
import threading
import Queue

class Player(threading.Thread):

    def __init__(self, messageQueue, metaint):
        threading.Thread.__init__(self)

        self.metaint = metaint

        self.messageQueue = messageQueue
        self.device = alsaaudio.PCM()
        self.rate = 44100
        self.famesize = self.rate 
        self.device.setrate(self.rate)
        self.buffer = ""

    def sendPCM(self):
        print("Buffer length: " + str(len(self.buffer)))
        if len(self.buffer) > self.metaint + 255:
            pcmData = self.buffer[:self.metaint]
            self.device.write(pcmData)
            self.buffer = self.buffer[self.metaint:]
            print ("New buffer length 1: " + str(len(self.buffer)))
            metaDataLength = ord(self.buffer[:1]) * 16
            print ("Metadata length: " + str(metaDataLength))
            self.buffer = self.buffer[1:]
            print ("New buffer length 2: " + str(len(self.buffer)))
            metaData = self.buffer[:metaDataLength]
            print len(metaData)
            self.buffer = self.buffer[metaDataLength:]
            print ("New buffer length 3: " + str(len(self.buffer)))


    def run(self):
        self.sendPCM()
        while True:
            message = self.messageQueue.get()
            if message: self.buffer += message
            self.sendPCM()
            self.messageQueue.task_done()

def getResponseHeaders(socket):
    data = socket.recv(1024)

    while not "\r\n\r\n" in data:
        data = data + socket.recv(1024)

    return data

def getHeaders(response):
    headers = {}
    for line in response.splitlines():
        if line == '\r\n':
            break # end of headers
        if ':' in line:
            key, value = line.split(':', 1)
            headers[key] = value

    return headers


HOST = 'bbcmedia.ic.llnwd.net'
GET = '/stream/bbcmedia_lc1_radio1_p?s=1420917253&e=1420931653&h=1ff16ea945bd420669c48ae72d003c09'
PORT = 80

#create an INET, STREAMing socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((HOST, PORT))
client_socket.send("GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent:%s\r\nIcy-MetaData:%s\r\nRange:%s\r\n\r\n" % (GET, HOST,"VLC/2.0.5 LibVLC/2.0.5", "1", "bytes=0-"))

responseHeaders = getResponseHeaders(client_socket)
headers = getHeaders(responseHeaders)
metaint = int(headers['icy-metaint'])
br = int(headers['icy-br'])
print (metaint)



queue = Queue.Queue()

player = Player(queue, metaint)
player.daemon = True
player.start()

while 1:
    queue.put(client_socket.recv(4096))


client_socket.close()
sys.exit(0)

Upvotes: 0

Views: 2016

Answers (1)

Brad
Brad

Reputation: 163234

It doesn't look like you are actually decoding the audio data.

You are attempting to demux the audio data from the metadata, but you must also run the audio data through the codec to get PCM samples. SHOUTcast/Icecast servers do not send raw PCM. They usually use MP3 or AAC wrapped in ADTS.

I'm not a Python coder so I do not know what all you have available to you. An easy way to decode is to use FFmpeg. It supports STDIO, so you can easily pipe data to it and let it handle the stream and return PCM samples.

Upvotes: 1

Related Questions