In letzter Zeit arbeite ich wieder ein bisschen an meinen ganzen MP3-Tools, unter anderem weil mir vorschwebt, in nächster Zeit ein Saugnapf-Podcast zu machen. Und da kommt es oft vor, dass ich mir ein kleines Stück aus einem grossen MP3 raussplitten muss. Die meisten MP3-Tools die es so gibt, packen das nicht, auf das Bit-Reservoir bei MP3-Frames zu achten, so dass die geschnittenen MP3s dann knarzen und fiepen und generell kaputt sind (siehe dazu auch die Erklärung in RFC 3119). Nebenbei angemerkt: der Algorithmus in RFC 3119 zum Extrahieren von ADUs ist leicht kaputt, meine Implementierung in poc müsste aber korrekt sein (zumindest wurde dies “empirisch” belegt :).
Gestern wurde also die Version 0.4.0 von poc released, diesmal mit ID3 Tag Support für mp3cue, und ein neues Tool namens mp3cut, dass es ermöglicht, von der Kommandozeile aus MP3s zu schneiden, und zwar nicht interaktiv (also nach dem Motto “Schneide bitte die 5 erste Minuten raus”). Ob das jetzt wirklich brauchbarer ist als eine GUI ist mir noch nicht wirklich klar. Beim Debuggen habe ich dann auch einen lustigen Bug gefunden. Beim Schneiden von dem submix 027 von Subsource kam ich auf relativ komische Ergebnisse. Mein iTunes sagte mir, dass das Stück, das ich herausschneiden wollte, bei 29:30 anfing. Prompt werfe ich mp3cut an, doch da kommt nicht wirklich das heraus, was ich vorhin gehört habe. Zeigt denn jetzt iTunes die falsche Zeit an, oder ist mein Tool nicht wirklich kosher? Ich vermute natuerlich zuerst, dass mein Tool kaputt ist, und schreibe da ein paar Debugtools. Doch dann komme ich auf die Idee, die 10 ersten Sekunden von dem Submix rauszuschneiden. Nach dem Importieren in iTunes zeigt dieser mir eine Länge von 15 Sekunden an. Aha! Stoppuhr in die Hand, auf Play druecken. Siehe da, die Sekunden von iTunes sind ploetzlich kuerzer geworden. Beim Testen mit anderen Playern waren alle falsch (winamp, xmms, iTunes), nur meine Software nicht. Na super!
Nun aber zu dem eigentlichen Thema von diesem Posting. poc ist eigentlich ein Sammlung von Streamingtools (hauptsächlich für mp3s, aber ogg über HTTP geht schon). Das ursprüngliche Ziel war Streamen über Multicast, und das baut auf UDP auf, so dass es relativ schnell bei bisschen kaputtem Netz zu Packet Loss kommen kann. Die “Standard”-Lösung für Streaming über Multicast ist das Real-Time Transport Protocol, bei dem es zwei genauere Standard für das Streaming von MP3s über RTP gibt: RFC 2250 und RFC 3119. Bei RFC 2250 wird der straightforward Ansatz verwendet: ein Frame pro UDP Paket (so ungefähr). Bei Packet Loss klingt das aber richtig, richtig scheisse. Kennt ihr noch die ganz alten MP3 Player, die noch keine Tags erkennen konnten (z.B. mpg123)? Da hat das dann immer so *quieek* *flutsch* gemacht, oder noch besser, der Player ist ausgestiegen. Genauso klingt RTP RFC 2250, weil MP3 Frames meistens noch Daten aus vorherigen MP3 Frames benötigen. RTP RFC 3119 will das verbessern, und versendet nur ein “ADU” pro UDP Paket. Ein ADU ist ein selbstständiges MP3 Frame, bei der keine Daten aus vorherigen Paketen benötigt werden. Bei Paket Loss kommt es nur zu einem Aussetzer in der Klangwiedergabe, aber nicht zu Fehlern in der MP3 Dekodierung. Keins der beiden Protokoll ermöglicht jedoch das Ausradieren von Packet Loss, so dass ich da ein kleines Protokoll entworfen habe, das mit Forward Error Correction arbeitet, um Packet Loss beim MP3 Streaming zu vermeiden versucht. Dazu habe ich ein Verfahren von Luigi Rizzo eingesetzt, dass auf linearen Operationen basiert, und ein “Erasure Code” ist, also genau geeignet ist, um Packet Loss zu vermeiden. Das Verfahren ist in dem Paper “Effective erasure codes for reliable computer communication protocols” beschrieben.
Im Grossen und Ganzen funktionniert das Streamingverfahren wie folgt: aus einem MP3 Strom werden die ADUs extrahiert (so wie bei RFC 3119 RTP). Diese ADUs werden in Gruppen von k ADUs gruppiert, und aus diesen k ADUs werden anhand des FEC-Verfahrens n Pakete gebildet. Dabei sind die k ersten Pakete die ADUs vom Anfang. Jetzt werden die n Pakete mit einer Sequenznummer versehen und über UDP versendet. Ein Empfänger braucht nur k dieser Pakete zu empfangen, um die k ADUs wieder herzustellen. Es reicht also k von n Pakete zu empfangen, um den Stream lückenlos abzuspielen. Das Coole ist, dass man k und n frei wählen kann, und während des Streamens auch anpassen kann. Ein weiteres cooles Feature ist, dass selbst wenn nicht alle k Pakete empfangen werden können Teile des Streams abgespielt werden können. Wenn die empfangenen Pakete zu den k ersten Paketen der n Paketen gehören (also den original-ADUs gleich sind), können sie wie bei RFC 3119 abgespielt werden. Die jetzigen Tools (poc-fec, der Streamserver, und pob-fec, der Streamclient) haben allerdings keinen eingebauten MP3-Decoder, sondern kleben die empfangenen ADUs wieder zusammen zu einem “korrekten” MP3-Stream, und schreiben diesen auf die Standardausgabe, damit man sie von dort in ein weiteres Tool streamen kann. Das sieht dann so aus:
pob-fec | mpg123 –
oder auch
pob-fec | poc-http –
Das erste leitet die Standardausgabe in das supergrottenschlechte Tool mpg123 um (unter Debian sogar in das noch schlechtere, aber *FREIE* Tool mpg321), das zweite in den HTTP Streamserver um. Das ist aber alles ein bisschen arg viel Overhead, weil der MP3 Decoder dann aus diesem Stream wieder die ADUs rauspopeln muss. Da koennte man sich den Zwischenschritt doch sparen, und deswegen bastel ich gerade an einem integrierten MP3-FEC-Player. Doch dazu mehr in dem nächsten Netzstaub-Posting.
PS: Wie ich gerade gemerkt habe, gibt es zu FEC und RTP FEC neue RFCs: RFC 3452 und ein IETF Draft.