Die Programmierung von Multimedia-Anwendungen unter Linux führt über steinige Wege. Zahlreiche Bibliotheken sind zu bändigen und mitunter ist die Hardware sogar direkt anzusprechen. OpenML macht damit nun endgültig Schluss – zumindest wenn es nach dem Willen der Khronos Group geht.
Kurz nach der Jahrtausendwende schlossen sich einige namhafte Unternehmen aus dem Multimedia-Bereich in der Khronos Group zusammen. 3Dlabs, ATI, Discreet, Intel, Nvidia, SGI und Sun Microsystems wollten nach dem Vorbild des schlanken und erfolgreichen OpenGL eine plattformübergreifende Multimedia-Bibliothek schaffen. Dank einer einheitlichen Schnittstelle sollten Programmierer in Zukunft keine Gedanken an die vorhandene Hardware und ihre Ansteuerung verschwenden müssen.
Späte Implementierung
Bereits Mitte 2001 lag die Spezifikation 1.0 der Open Media Library [1], kurz OpenML, vor – nicht verwandt und nicht verschwägert mit OpenMP, einer Bibliothek zur Programmierung von Nebenläufigkeiten, und OpenAL, einer Bibliothek für die Tonwiedergabe. Damit standen die Schnittstellen und die interne Funktionsweise fest, die Mitglieder der Khronos-Gruppe (siehe Kasten “Khronos Group”) feierten, versprachen auf allen möglichen Konferenzen ihre Unterstützung, tranken Tee und warteten ab. Die Entwickler auf der ganzen Welt wussten nun zwar, wie die Schnittestelle aussieht, eine konkrete Implementierung der Bibliothek oder gar passende Treiber und Hardware ließen jedoch lange auf sich warten.
|
Khronos Group |
|---|
|
Die Khronos Group wurde im Januar 2000 ins Leben gerufen. Seither entwickelte sie verschiedene Multimedia-Schnittstellen im Stile von OpenGL. Die bekannteste dürfte OpenGL ES sein, eine auf eingebettete Systeme (Embedded Systems) zugeschnittene OpenGL-Implementation. Ende letzten Jahres übernahm das Konsortium schließlich auch die Pflege und Weiterentwicklung des normalen OpenGL-Standards. Die Khronos Group setzt sich aus drei Gruppen zusammen, die je nach eingebrachtem Kapital unterschiedlich viel bei der Standardisierung mitreden dürfen: Anwendungsentwickler können die OpenML-Spezifikationen und die Referenzimplementierung (SDK) kostenlos verwenden. Sie erhalten zudem die Erlaubnis, die Spezifikation unentgeltlich zu implementieren und zusammen mit anderen Produkten auszuliefern. Wer mag, darf auch das werbewirksame OpenML-Logo lizenzfrei auf seine Homepage kleben. Gegen eine jährliche Gebühr erhalten Interessierte eine offene Mitgliedschaft und werden zu so genannten Contributors. Dieser Rang gestattet ihnen die aktive Teilnahme an den verschiedenen Arbeitsgruppen (Working Groups) und somit eine gewisse Mitbestimmung. Promoters (Promotoren; Steuerungsgruppe) schließlich sind vor allem die Gründer der Khronos-Gruppe, namentlich jene, die ein dickes Portemonnaie mitbringen. Sie erhalten ein Vetorecht und legen die Ziele und Vorgehensweisen der Organisation fest. |
Retter in der Not
Glücklicherweise basieren wesentliche Teile der OpenML auf Vorleistungen des Grafikspezialisten Silicon Graphics (SGI). Der hatte bereits das Digital Media Handling SDK (DM SDK) im Angebot, das in die neue Multimedia-Bibliothek einfloss. Das erklärt vermutlich auch die recht schnelle Verabschiedung des neuen Standards. SGI jedenfalls packte die Gelegenheit beim Schopf und entwickelte das DM SDK flugs zum OpenML SDK weiter. Ab hier übernahm wieder die Khronos Group und erklärte das Ergebnis zur Referenzimplementation. Um die Verbreitung zu fördern, spendierte man ihr die SGI Free Software License C (Free C), die auch einen kostenlosen kommerziellen Einsatz gestattet [2].
Mittlerweile war es 2004 geworden. Seitdem schlummert die Referenzimplementierung fast unbemerkt auf Sourceforge vor sich hin. Lediglich das semiprofessionelle Videoschnitt- und Compositing-Programm Jahshaka (Abbildung 1) hält einsam die Fahne hoch, wenngleich es wegen seiner vermurksten Bedienung und der häufigen Abstürze nicht gerade als Aushängeschild gilt [3].

Abbildung 1: Das Video-Effekt-Programm Jahshaka setzt auf OpenML. Die grafische Oberfläche greift deshalb auf OpenGL und zurück und macht von der Hardwarebeschleunigung einer Grafikkarte Gebrauch.
Vorteile
Gegenwärtig dümpelt der OpenML-Gedanke weiter vor sich hin. Eigentlich zu Unrecht, denn die Bibliothek ist durchaus einen Blick wert. So vereinheitlicht und vereinfacht sie nicht nur den Zugriff auf sämtliche Multimedia-Geräte eines Computers, sie hilft auch gleich noch bei der Synchronisierung der Medien und vereinfacht den Umgang mit dem Pufferspeicher (Buffer). Das Ganze unabhängig vom Betriebssystem und kostenlos: Jeder darf die Spezifikationen in eine Bibliothek gießen und die bereits vorhandene Referenzimplementierung in seinen Produkten nutzen, egal ob es sich um ein kommerzielles oder ein Open-Source-Projekt dreht.
Ähnlich wie OpenGL zieht OpenML nur eine dünne Abstraktionsschicht über die vorhandene Multimedia-Hardware. Der Programmierer spricht sie über ein schlankes C-API an, das nicht nur einfacher zu handhaben ist als Hardware, sondern sich auch leichter auf Systeme mit wenigen freien Ressourcen portieren lässt – eingebettete Systeme sind da nur ein Beispiel. Ähnlich wie 3D-Engines auf OpenGL zurückgreifen, könnte eine spezialisierte Highlevel-Bibliothek auf OpenML aufsetzen, was wiederum Vielfalt und Flexibilität schafft.
Kerniges
Dass die OpenML-Väter in erster Linie bestehende Standards zu einem neuen zusammengemischt haben (siehe Abbildung 2), führt auch dazu, dass OpenML nun aus mehreren Komponenten oder Teilen besteht, die jeweils für eine spezielle Aufgabe zuständig sind (siehe Abbildung 3).

Abbildung 2: OpenML entstand aus drei bereits existierenden Bibliotheken, die alle vom Hersteller SGI stammen. Lediglich die Komponente für die Synchronisation kam neu hinzu.

Abbildung 3: OpenML besteht aus vier Komponenten: Neben einem erweiterten OpenGL sind dies noch die Gerätesteuerung Mldc, eine Synchronisierungsschicht und die eigentliche Kernkomponente ML. Letztere greift auf die physische Hardware über so genannte Device-Module zu.
Den Kern bildet die Media Library, kurz ML. Sie entspricht weitgehend dem von SGI entwickelten Digital Media Handling SDK. Die von ihr zur Verfügung gestellten Funktionen holen das Video- und Audiomaterial von angeschlossenen Mediengeräten ab, verarbeiten die Multimediadaten weiter, synchronisieren sie bei Bedarf und zeigen sie auf einem geeigneten Ausgabegerät an.
Der Kernbibliothek steht die OpenML Display Control Library (Mldc) zur Seite. Sie steuert eines oder mehrere Ausgabegeräte, zum Beispiel den Monitor, einen Fernseher oder gleich eine ganze Monitorwand. Darüber hinaus sorgt sie für deren Feintuning, indem sie beispielsweise das Videoformat ändert oder die Gammakorrektur anwirft. Eine Anwendung kann somit genau kontrollieren, was auf welchen Monitoren dargestellt wird. Auch die Mldc hat ihren Ursprung bei SGI, genauer gesagt in den XSGIvc-Erweiterungen für X11.
Ein Muss: OpenGL
Als dritte bekannte Komponente holte die Khronos Group das bekannte OpenGL ins Boot, das um ein paar zusätzliche Extensions aufgebohrt wurde. Letztere sorgen für die Verbindung von 3D-Grafik und Video und müssen folglich von OpenML-konformen Grafikkarten implementiert werden. Beispielsweise lassen sich so Grafiken oder Videos als Texturen nutzen, schnell Farben korrigieren, eine Bildverbesserung aktivieren oder Farbräume konvertieren. Darüber hinaus ermöglichen sie die Nutzung von Interlaced-Material (Zeilensprungverfahren, wie es beispielsweise die deutsche Fernsehnorm PAL vorgibt, [4]).
Synchronisierung
Die letzte OpenML-Komponente hilft schließlich bei der Synchronisation aller Datenströme. Dazu lässt sie im Hintergrund gleich mehrere Stoppuhren mitlaufen. Den Ausgangspunkt bildet dabei die Unadjusted System Time (UST), hinter der sich nichts anderes als die Systemzeit verbirgt. Zusätzlich nummeriert OpenML in jedem Datenstrom jedes Sample durch. Bei einem Video würde somit jedes Einzelbild nummeriert, in Audiomaterial jedes Sample.
Eine solches Zählsystem bezeichnet OpenML als Media Stream Counter, kurz MSC. Das Verhältnis der MSC zur UST ergibt somit die Wiedergabefrequenz. Bei Videomaterial wären dies beispielsweise 25 Bilder pro Sekunde. Auf dieser Basis lässt sich somit recht einfach eine Synchronisierung erzielen oder gegebenenfalls korrigieren.
Nach so viel Theorie soll ein kleines Beispiel die Verwendung von OpenML illustrieren. In Anlehnung an [5] soll es ein Audiosignal über die Soundkarte ausgeben. Die Audiodaten wurden bereits von der Festplatte eingelesen und im Hauptspeicher abgelegt – OpenML leistet hierbei leider keine Hilfe. Wie allgemein üblich bezeichnet man den dabei reservierten Speicherblock als Buffer.
Köpfchen
Wie bei jedem C-Programm müssen zuerst die passenden Header her. Bei einem OpenML-Programm gehören dazu:
- »ml.h«, der Zugriff auf die Kernbibliothek
erlaubt. - »mlu.h«, der ein paar weitere Funktionen
bereitstellt, die auf der Kernbibliothek aufbauen.
Alle Multimedia-Geräte, die eine Anwendung nutzen kann, verzeichnet OpenML schön übersichtlich in einer Hierarchie, dem so genannten Capability Tree. Er enthält nicht nur die Geräte selbst, sondern auch noch ein paar zusätzliche Informationen über ihre Fähigkeiten. Das beim OpenML SDK mitgelieferte Hilfsprogramm »mlquery« spuckt eine Liste aller nutzbaren Geräte auf dem aktuellen Rechner aus. Abbildung 4 zeigt ein Beispiel dafür.

Abbildung 4: Das Hilfsprogramm »mlquery« spuckt eine Liste mit allen im System vorhandenden Multimediageräten aus. Hier hat es eine zum Audio-Subsystem OSS kompatible Soundkarte gefunden.
Klettergerüst
Um ein passendes Gerät für die geplante Operation im Capability Tree zu finden, durchläuft die Anwendung die Hierarchie-Ebenen, bis sie auf einen geeigneten Kandidaten stößt. Als Einstiegspunkt dient immer die oberste Ebene (in der Baumansicht also die Wurzel), die wiederum das gesamte Computersystem repräsentiert. Im Beispiel wird ein Gerät für die Audio-Ausgabe gesucht:
MLint64 devId=0; mluFindDeviceByName(ML_SYSTEM_LOCALHOST,"audio device", &devId);
Danach steht in der Variablen »devId« das passende physische Gerät, »ML_SYSTEM_LOCALHOST« bezeichnet das gesamte System und somit den Einsprungspunkt des Capability Tree.
Steckplätze
Leider kann ein Programmierer die Audiodaten nicht einfach gedankenlos an die Soundkarte weiterreichen. Was ist zum Beispiel, wenn die Soundkarte mehrere Ausgänge bereitstellt? Deshalb besitzt jedes physische Gerät ein oder mehrere logische Geräte, die so genannten Jacks (auf Deutsch: Buchsen). Sie repräsentieren die Orte, an denen ein Video oder die Audiodaten landen können oder von denen sie in das System gelangen. Beispiele für ein Jack wären im Fall der Soundkarte ein Mikrofon oder der Audio-Ausgang. Ein Jack stellt somit für die Anwendung die eigentliche Schnittstelle ins oder aus dem System bereit.
Virtuelle Geräte
Ein Jack muss übrigens nicht zwangsweise mit einer physischen Schnittstelle übereinstimmen. Da es sich um ein logisches Gerät handelt, könnte ein Jack auch durchaus mehreren realen Anschlüssen entsprechen (zum Beispiel bei Mehrkanalton), umgekehrt können mehrere Jacks zu einer physischen Schnittstelle gehören. Das Beispiel benötigt also einen Jack für die Ausgabe des Audiomaterials. Der folgende Code ermittelt einfach den ersten verfügbaren Ausgang:
MLint64 jackId=0; mluFindFirstOutputJack(devId, &jackId);
Als Nächstes braucht der Programmierer einen Trichter, der das Audio- beziehungsweise Videomaterial in den Jack und somit letztlich aus dem Computersystem in die reale Welt leitet. Dieser Trichter heißt in der OpenML-Terminologie Path und verbindet den Puffer der Audiodaten im Hauptspeicher mit dem Jack (siehe Abbildung 5).

Abbildung 5: Die Multimedia-Anwendung speichert die Mediadaten in einem Buffer, der anschließend über einen Path zum Jack geschoben wird. Letzterer bildet die Schnittstelle ins beziehungsweise aus dem Computersystem.
Generell unterscheidet OpenML zwischen:
- Input Path, der Datenströme liefert und in einem
Pufferspeicher (Buffer) ablegt. - Output Path, der einen Speicherbereich von der Anwendung
entgegennimmt und dessen Inhalt an den Jack weiterreicht.
Im Beispiel stellt folgender Code über einen Path eine Verbindung zum Ausgabe-Jack her:
MLint64 pathId=0; mluFindPathToJack(jackId, &pathId);
Anschließend öffnen diese beiden Zeilen den so erzeugten Output Path:
MLopenid openPath; mlOpen(pathId, NULL, &openPath);
Damit erhält die Anwendung eine direkte Verbindung zur Hardware. Zusätzlich reserviert die Funktion die notwendigen Systemressourcen für die nachfolgenden Operationen. Der zweite Parameter in »mlOpen()« dient zur Übergabe von Konfigurationsparametern, in diesem Fall gelten die Voreinstellungen.
Postbote
Wenn eine Verbindung zum Ausgabegerät steht, sind ein paar Einstellungen vorzunehmen. Die auszugebenden Audiodaten wurden mit einer Frequenz von 44100 Hz in Mono aufgezeichnet. Damit die Wiedergabe nicht verzerrt ertönt, muss der Programmierer dem Ausgabegerät diese Einstellungen mitteilen. In OpenML kommunizieren Anwendungen mit den Geräten über Nachrichten. Die Nachrichten bestehen aus einer Liste von Parameter- und Wertepaaren, die wiederum in einem Array vom Typ »MLpv« landen. Die Parameter und einige Werte definieren die Headerdateien in Form von Makros. Das Ende der Liste markiert stets ein »ML_END«.
Im Beispiel setzt der Programmierer also zunächst eine neue Nachricht zusammen, welche die gewünschten Einstellungen enthält (16 Bit, 44100 Hz, Mono, mit einer Dämpfung (Gain) von -12 dB), siehe Listing 1. Diese Nachricht schickt er anschließend über den geöffneten Path an den Jack:
mlSetControls(openPath, controls);
Dabei handelt es sich um einen blockierenden Aufruf. Die Funktion kehrt also erst dann zurück, wenn die Einstellungen angekommen sind oder wenn ein Fehler auftritt.
|
Listing 1: |
|---|
01 MLpv controls[5]; /* Nachricht, die 5 Werte enthält */ 02 MLreal64 gain = -12; /* Dämpfung */ 03 04 controls[0].param = ML_AUDIO_FORMAT_INT32; 05 controls[0].value.int32 = ML_FORMAT_S16; 06 controls[1].param = ML_AUDIO_CHANNELS_INT32; 07 controls[1].value.int32 = 1; 08 controls[2].param = ML_AUDIO_GAINS_REAL64_ARRAY; 09 controls[2].value.pReal64 = &gain 10 controls[2].length = 1; 11 controls[3].param = ML_AUDIO_SAMPLE_RATE_REAL64; 12 controls[3].value.real64 = 44100.0; 13 controls[4].param = ML_END; |
Massenwanderung
Jetzt endlich kann der Anwender die Audiodaten an das Gerät schicken. Auch sie werden in einer Nachricht verpackt und über den Path zum Gerät gesandt. Im Beispiel markiert der Zeiger »ourAudioBuffer« den Anfang des Puffers mit den Audiodaten. Wo genau die über »ourAudioBuffer« zu erreichenden Daten im Speicher liegen und wie sie aufgebaut sein müssen, verrät der SDK-Reference-Guide [6]. Bei Stereo-Ton etwa folgt immer auf ein Sample des linken Kanals eines des rechten (Interleaved).
Da der Anwender selbst für das Befüllen und die Erstellung der Buffers zuständig ist, haftet er folglich persönlich für die Einhaltung dieser Rahmenbedingungen. Sobald sie stimmen, kann er die Audiodaten hinter »ourAudioBuffer« in eine Nachricht packen (Listing 2).
|
Listing 2: |
|---|
01 MLpv msg[2]; 02 msg[0].param = ML_AUDIO_BUFFER_POINTER; 03 msg[0].value.pByte = ourAudioBuffer; 04 msg[0].length = sizeof(ourAudioBuffer); 05 msg[1].param = ML_AUDIO_UST_INT64; /* Zeitstempel */ 06 msg[2].param = ML_END; |
Schließlich schickt er auch sie durch den Pfad auf die Reise:
mlSendBuffers(openPath, msg);
Die Funktion wirft die Nachricht jedoch nur in den Briefkasten des Path. Damit er auch geleert wird, ist noch ein weiterer Schritt nötig:
mlBeginTransfer(openPath);
Erst damit öffnet OpenML den Briefkasten und schickt die Nachrichten in der Reihenfolge ihres Eingangs an das Gerät. Der Einsatz einer solchen Warteschlange (Message Queue) sieht auf den ersten Blick etwas überflüssig aus. Doch lassen sich nur auf diese Weise ständig auftretende Verzögerungen (Latenzen) oder Unterbrechungen, wie sie beispielsweise das Betriebssystem regelmäßig verursacht, ausreichend kompensieren.
Kein Locking
Da »mlBeginTransfer()« sofort zurückkehrt und nicht etwa wartet, bis die Ausgabe abgeschlossen ist, darf die Anwendung in der Zwischenzeit andere nützliche Dinge erledigen. Doch Vorsicht: OpenML kennt kein Locking, man sollte also auf den übersandten Buffer nicht mehr zugreifen. Deshalb legt sich die Beispielanwendung der Einfachheit halber mit »sleep(5)« schlafen.
Sobald die Daten verarbeitet sind, schickt OpenML eine Erfolgsnachricht an den Briefkasten der Anwendung, den diese wiederum per »mlReceiveMessage()« öffnet:
mlReceiveMessage(openPath, &messageType, replyMessage);
if(messageType==ML_BUFFERS_COMPLETE)
printf("Daten übertragen!n");
Über diesen Mechanismus gelangt der Programmierer auch an möglicherweise anfallende Fehlermeldungen. Zusätzlich liefert jede Funktion noch einen Rückgabewert vom Typ »MLstatus«. Das klappt jedoch bei der hier gezeigten asynchronen Übertragung nur bedingt. Zum guten Ende schließt
mlClose(openPath);
den Path und gibt damit den Jack wieder frei. Wie man mit größeren Datenmengen geschickt umgeht, verrät der Kasten “Presswurst”.
|
Presswurst |
|---|
|
Sind die zum Jack fließenden Datenpakete recht groß oder in einer hohen Anzahl vorhanden, sollten Programmierer am besten einen kleinen Buffer anlegen, diesen abspielen und sich dann das so genannte Wait Handle zurückgeben lassen, gewissermaßen die Wartenummer der Nachricht: MLwaitable pathWaitHandle; mlGetReceiveWaitHandle(openPath, &pathWaitHandle); Wie auf einem Amt darf das Programm nun warten, bis die Nummer an der Reihe ist: fd_set fdset; FD_ZERO(&fdset); FD_SET(pathWaitHandle, &fdset); select(pathWaitHandle+1, &fdset, NULL, NULL, NULL); Anschließend wartet das Programm erneut, bis die entsprechende Bestätigungsnachricht eintrifft. Jetzt füllt man den Buffer möglichst schnell mit den nächsten Daten – beispielsweise Bildern oder dem nächsten Audiostück – und schickt ihn im alten Umschlag einfach wieder zurück: mlSendBuffers(openPath , replyMessage); Mit dieser Technik lassen sich auch größere Datenmengen verlässlich verarbeiten. |
Röhren und Transcoder
Neben den Jacks kennt OpenML noch die so genannten Transcoder. Sie sind ebenfalls logische Geräte, die Daten aus einem Buffer entgegennehmen, dann eine Operation auf ihnen ausführen und das Ergebnis wieder in einen anderen Puffer hinausschieben (Abbildung 6). Transcoder dienen vorrangig dazu, die Mediadaten zwischen Jacks zu verschieben. Zum Beispiel kann ein Jack Daten einer Videokamera liefern, die durch einen passenden Transcoder fließen, dort dekomprimiert werden und schließlich über einen weiteren Jack auf einen Vorschaumonitor wandern. Transcoder dürfen in Hardware- (beispielsweise zur Berechnung von Echtzeit-Video-Effekten) oder in Software (etwa Mpeg-2-Dekodierer) realisiert sein.

Abbildung 6: Ein Weg durch OpenML: Der Datenstrom fließt über den linken Jack und den verbundenen Path in einen Puffer. Von dort saugt ihn eine Pipe durch einen Transcoder, der das Ergebnis über eine Pipe in einen Puffer im Hauptspeicher pumpt. Die Ausgabe erfolgt über einen weiteren Path zum rechten Jack.
Während ein Jack über einen Path mit Daten und Nachrichten versorgt wird, übernimmt diese Aufgabe bei einem Transcoder die so genannte Pipe (Röhre). Zu einem Transcoder gehören immer mindestens eine Eingabe-Pipe, über die er die Eingabedaten bekommt, sowie eine Ausgabe-Pipe, über die er das Ergebnis abliefert.
Fazit
In der Tradition von OpenGL hat Open- ML das Zeug zu einem plattformübergreifenden Standard für Multimedia-Anwendungen. Nicht umsonst wurde und wird die Bibliothek immer wieder als Konkurrent zu Microsofts Direct X gehandelt. Was alleine noch fehlt, ist die Unterstützung durch Hard- und Softwarehersteller.
Doch ist auch OpenML keine eierlegende Wollmilchsau, die jeden denkbaren Bereich abdeckt. So fehlen beispielsweise Funktionen für das Streaming von Multimedia-Daten über ein Netzwerk oder die Unterstützung von speziellen Eingabegeräten, wie sie nicht nur in Form von Gamepads oder Lenkrädern in Spielen auftauchen, sondern auch häufig in Multimedia-Terminals zum Einsatz kommen. Auch Linux-spezifische Schnittstellen wie Alsa-Geräte und digitales Video unterstützt OpenML nur unzureichend. Nicht zuletzt bläst OpenML im Audiobereich aus der Ecke der OpenAL-Anwender und -Programmierer ein starker Wind entgegen. (ofr)
|
Infos |
|---|
|
[1] OpenML: [http://www.khronos.org/openml] [2] SGI Free Software License C: [http://oss.sgi.com/projects/FreeC] [3] Jahshaka: [http://www.jahshaka.org] [4] Informationen zur PAL-Norm: [http://de.wikipedia.org/wiki/PAL] [5] OpenML Media Library Software Development Kit, Beginner\’s Guide: [http://techpubs.sgi.com/library/tpl/cgi-bin/download.cgi?coll=0650&db=bks&docnumber=007-4376-001] [6] OpenML SDK: [http://sourceforge.net/projects/oml-ri] |






