Dienstag, 4. September 2012

Zotac Box mit HDMI Audio

HDMI Audio ist unter Linux und speziell unter Debian immer ein wenig kompliziert in Betrieb zu nehmen.
Ich beschreibe hier kurz wie ich meine Zotac Box, die mit einem NVIDIA Ion Chipset ausgerüstet ist eingerichtet habe. Auf der Box läuft ein Debian Squeeze (6.0.3) und MythTV 0.24.
Das alsa Package und das alsa-utils Package müssen installiert sein.
Mit aplay -l werden alle Audiodevices aufgelistet. Das sieht bei mir so aus:
root@twinpeaks:~# aplay -l
**** Liste der Hardware-Geräte (PLAYBACK) ****
Karte 0: Intel [HDA Intel], Gerät 0: ALC888 Analog [ALC888 Analog]
  Sub-Geräte: 1/1
  Sub-Gerät #0: subdevice #0
Karte 0: Intel [HDA Intel], Gerät 1: ALC888 Digital [ALC888 Digital]
  Sub-Geräte: 1/1
  Sub-Gerät #0: subdevice #0
Karte 1: NVidia [HDA NVidia], Gerät 3: HDMI 0 [HDMI 0]
  Sub-Geräte: 1/1
  Sub-Gerät #0: subdevice #0
Karte 1: NVidia [HDA NVidia], Gerät 7: HDMI 0 [HDMI 0]
  Sub-Geräte: 1/1
  Sub-Gerät #0: subdevice #0
Karte 1: NVidia [HDA NVidia], Gerät 8: HDMI 0 [HDMI 0]
  Sub-Geräte: 1/1
  Sub-Gerät #0: subdevice #0
Karte 1: NVidia [HDA NVidia], Gerät 9: HDMI 0 [HDMI 0]
  Sub-Geräte: 1/1
  Sub-Gerät #0: subdevice #0

Welches der vielen Audiodevices das Richtige ist habe ich durch probieren ermittelt. Hierzu eigenet sich das in den alsa-utils enthaltene speaker-test Programm.
HDMI Kabel an den Fernseher anschließen. Lautstärke des Fernsehers beachten, also nicht zu niedrig einstellen.
Anschließend für jedes der aufgelisteten Geräte das Kommando:
speaker-test -Dplughw:<Karte>,<Gerät>
ausführen. Wobei <Karte> durch die Nummer der Karte und <Gerät> durch die Nummer des Gerätes ersetzt werden müssen. Also das Kommando um das erste Gerät der Liste zu testen wäre dann:
speaker-test -Dplughw:0,0
Bei mir war es plughw:1,7. Hat man das Device identifiziert kann man es als Alsa Default Device vereinbaren, in dem man die Datei /etc/asound.conf wir folgt anlegt:

pcm.!default {
    type plug
    slave.pcm {
        type hw
        card 1
        device 7
    }
}

Ich würde das Alsa System jetzt neu starten. 
Für alle MythTV Anwender: In der Audiokonfiguration wird das Device mit ALSA:plughw:1,7, natürlich entsprechend eurer Karte/Gerät, angegeben.

Einfacher WMA nach MP3 Encoder

Da einige der Internetradiosender ihre Sendungen nur als  WMA-Stream ausstrahlen, mein N770 Webtablet aber nur MP3 wiedergeben lann habe ich, natürlich nur zu Bildungszwecken, das folgende kleine Skript geschrieben.
Es ist ein kleiner Webserver in Python der auf Port 8080 lauscht. Bei einem eingehenden Request, wobei es sich um einen beliebigen GET Request handeln muss, verbindet sich der Webserver mit der im run  Kommando angegebenen mms Adresse und lädt den WMA-Stream in einen Transcoder, der ihn dann in einen MP3 Stream umwandelt. Der aus dem Transcoder gewonnene Datenstrom wird mit dem notwendigen Contentheader versehen und als Antwort auf den GET Request zurücksendet.
Das Skript ist in Python 2.5 geschrieben und benötigt die libmimms bindings für Python.
Source radio.py:
from os import curdir, sep
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from libmimms.core import run

__author__="barmeier"
__date__ ="$04.01.2010 16:48:20$"


class MmsTranscoder(BaseHTTPRequestHandler):

    def do_GET(self):
        try:
            self.send_response(200)
            self.send_header('Content-type', 'audio/mpeg')
            self.end_headers()
            run("mms://radiotstation/livestream.wma",self.wfile)
        except IOError:
            self.send_error(404,'File Not Found: %s' % self.path)

def main():
    try:
        server = HTTPServer(('', 8080), MmsTranscoder)
        print 'Welcome to the machine...'
        server.serve_forever()
    except KeyboardInterrupt:
        print '^C received, shutting down server'
        server.socket.close()

if __name__ == '__main__':
    main()
Damit die automatische MP3 Kodierung funktioniert habe ich die download Methode in der Datei libmms/core.py gepatched. Das kann man sicher viel eleganter machen, aber ich wollte endlich Radio hören.
download Methode für libmms/core.py:
def download(url, outStream):
    "Using the given options, download the stream to a file."
    stream = libmms.Stream(url, 1e6)
    bitrate= 128000
    dec= enc= mx= None
    #sys.stdout.flush()
    dm = muxer.Demuxer("wma")
    type="mp3"
    stopped = False
    for data in stream:
        #f.write(data)
        frames = dm.parse(data)
        if frames:
            for fr in frames:
                # Assume for now only audio streams
                if dec == None:
                    # Open decoder
                    dec = acodec.Decoder(dm.streams[fr[0]])
                    print 'Decoder params:', dm.streams[fr[0]]
                # Decode audio frame
                r = dec.decode(fr[1])
                try:
                    if r:
                        if bitrate == None:
                            bitrate = r.bitrate
                        # Open muxer and encoder
                        if enc == None:
                            params = {'id': acodec.getCodecID(type),
                                'bitrate': bitrate,
                                'sample_rate': r.sample_rate,
                                'channels': r.channels}
                            print 'Encoder params:', params
                            mx = muxer.Muxer(type)
                            stId = mx.addStream(muxer.CODEC_TYPE_AUDIO, params)
                            enc = acodec.Encoder(params)
                            ss = mx.start()
                            outStream.write(ss)
                            outStream.flush()
                        enc_frames = enc.encode(r.data)
                        if enc_frames:
                            for efr in enc_frames:
                                ss = mx.write(stId, efr)
                                if ss:
                                    outStream.write(ss)
                                    outStream.flush()
                except:
                    print "Client lost. Waiting for next ..."
                    stopped=True
                    break
        if stopped:
            break
    stream.close()

Auf der Kommandozeile startet man das Programm mit:
sbamamoto/home$ python raodio.py
Einfach im Browser http://<rechnername>:8080 aufrufen und dann sollte es funktionieren.