202403_ffmpeg_wasm-cover

Flexibles Multimediawerkzeug mit WebAssembly & FFmpeg

Von am 06.03.2024

In der Welt der multimedialen Entwicklung ist FFmpeg, der Open-Source-Werkzeugkasten für Multimedia, längst ein bekannter Name. Dieser mächtige Werkzeugkasten ermöglichte bisher mühelos die Umwandlung von Mediendateien von einem Format in ein anderes, sei es per Kommandozeile oder durch die Integration in bestehende Projekte. Einziger Haken: Die Nutzung von FFmpeg im Webbrowser war bisher auf serverseitige Integration beschränkt, bei der Dateien auf dem Server hochgeladen und transkodiert werden. Doch dank der Entwicklung von WebAssembly (wasm) und der Umsetzung eines FFmpeg-Builds steht der Verwirklichung eines serverlosen Multimedia-Transcoders nichts mehr im Wege.

WebAssembly

WebAssembly eröffnet Entwickler:innen die Möglichkeit, leistungsstarke Funktionen aus anderen Sprachen direkt ins Web zu integrieren. In den vergangenen Jahren haben kreative Köpfe diese Möglichkeiten intensiv genutzt, um das volle Potenzial von WebAssembly auszuschöpfen.

WebAssembly ist im Wesentlichen ein binäres Anweisungsformat. Obwohl das zunächst kompliziert klingen mag, lässt es sich als eine gemeinsame Sprache definieren, die von Browsern verstanden und nahezu in nativer Geschwindigkeit ausgeführt werden kann. Im Gegensatz zu JavaScript, das hauptsächlich eine textbasierte Skriptsprache ist, ist WebAssembly als Low-Level-Sprache konzipiert, die es ermöglicht, Hochsprachen wie C, C++ oder Rust in ein binäres Format zu kompilieren, das im Browser ausgeführt werden kann.

Traditionelle Webanwendungen verlassen sich stark auf JavaScript für ihre Funktionalität. Obwohl JavaScript vielseitig und zugänglich ist, hat es bestimmte Einschränkungen bei leistungsintensiven Aufgaben wie 3D-Rendering, Videobearbeitung oder wissenschaftlichen Simulationen.

WebAssembly begegnet diesen Einschränkungen, indem es eine Möglichkeit bietet, solche Aufgaben mit auszuführen. Dies wird erreicht, indem WebAssembly näher am Maschinencode ist und Hardware-Ressourcen effizienter nutzt.

Funktionsweise von WebAssembly

Um WebAssembly besser zu verstehen, werfen wir einen vereinfachten Blick darauf, wie es funktioniert:

Kompilierung: Wir schreiben einen Code in einer Hochsprache wie C, C++, Rust usw. Dann kompilieren wir diesen Code in eine WebAssembly-Binärdatei (mit der Erweiterung .wasm). Diese Binärdatei ist stark optimiert für eine schnelle Ausführung.

Laden: Wenn wir eine Webseite laden, die ein WebAssembly-Modul enthält, lädt der Browser die .wasm-Datei genauso wie jede andere Ressource.

Ausführung: Einmal geladen, wird der WebAssembly-Code von der virtuellen Maschine des Browsers ausgeführt. Er läuft nahezu mit nativer Geschwindigkeit, da er dem Maschinencode näher ist, den die CPU Ihres Computers versteht.

Integration: WebAssembly kann nahtlos mit JavaScript interagieren. Wire können WebAssembly-Funktionen von JavaScript aus aufrufen und umgekehrt, was eine reibungslose Integration in bestehende Webanwendungen ermöglicht.

Vorteile von WebAssembly

Geschwindigkeit: Wasm erreicht im Browser nahezu native Leistung, indem es von gängigen Fähigkeiten der Hardware nutzt. Ein weiterer Faktor für die Geschwindigkeit von Wasm ist, dass der Code selbst ein effizientes Binärformat auf niedriger Ebene ist, das schneller von Browsern geladen und ausgeführt werden kann

Sicherheit: Wasm wird in einer speichersicheren, abgeschotteten Umgebung ausgeführt, die Datenkorruption und Sicherheitsverletzungen verhindern. Wasm gewährt keinen Zugriff auf die Umgebung, in der der Code ausgeführt wird. Wasm kann auf Systemressourcen zugreifen und E/A durchführen, wenn dies erlaubt wird.

Portabilität: Wasm ist plattformunabhängig, die Architektur von Wasm versucht, keine Annahmen über die zugrunde liegende Hardware zu treffen, auf der es ausgeführt wird. Dies bedeutet, dass Wasm darauf ausgelegt ist, sowohl von gängigen als auch von modernen Hardwarekomponenten zu profitieren. Wasm ist im Web immer verbreiteter geworden, aber es ist möglich, es außerhalb des Webs auszuführen, dank seiner plattformunabhängigen Architektur.

ffmpeg.wasm

Wie auf der GitHub-Seite des Projekts angegeben, ist ffmpeg.wasm eine WebAssembly- und JavaScript-Portierung von FFmpeg. Es handelt sich dabei um eine Open-Source-Software-Suite, die mehrere Bibliotheken und Programme zur Verwaltung von Audio-, Video-, Streams- und anderen Mediendateien enthält. All diese Tools können über die ffmpeg-Befehlszeile aufgerufen und ausgeführt werden. Für einen guten Einstieg in die Welt von FFmpeg kann folgenden Web-Guide der Association of Moving Image Archivists (AMIA) aus Los Angelese empfohlen werden. Hier werden die wichtigsten Bestandteile von FFmpeg anhand von praxistauglichen Beispielen veranschaulicht.

FFmpeg ist in C-Code programmiert und kann klarerweise nicht nativ im Browser ausgeführt werden. Wie funktioniert also ffmpeg.wasm? Auf einer höheren Ebene funktioniert ffmpeg.wasm, indem der ursprüngliche C-Code in WebAssembly transpiliert wird. Da WebAssembly im Browser ausgeführt werden kann, bringt es die Medienverarbeitungsfunktionen von FFmpeg direkt zum Client ins Web.

Daher ermöglicht ffmpeg.wasm die Verarbeitung von Video- und Audioinhalten in JavaScript-basierten Webanwendungen. Im Detail ermöglicht es Video- und Audioaufzeichnung, Formattranskodierung, Video- und Audio-Bearbeitung sowie Video-Skalierung. Obwohl es eine auf WebAssembly basierende Bibliothek ist, können wir diese in JavaScript-Code genauso verwenden wie jede andere npm-Bibliothek. Das ist die Stärke von Wasm. Da es nach Wasm übersetzt wird, können wir ffmpeg.wasm direkt in Ihrem Browser nutzen, ohne sich um Leistungsprobleme sorgen zu müssen.

Wenn Sie beispielsweise Transcoding-Anwendungen im Browser bereitstellen möchten, müssen Sie die Videoclips jetzt nicht mehr über das Netzwerk an einen Server senden. Stattdessen können Sie dies lokal und sogar ohne Internetverbindung durchführen.

Achitektur von ffmpeg.wasm

Multimedia-Transkodierung ist eine ressourcenintensive Aufgabe, die Sie nicht im Hauptthread ausführen möchten. Daher übertragen wir diese Aufgabe in ffmpeg.wasm standardmäßig an den Web Worker (ffmpeg.worker). Dadurch sind fast alle Funktionsaufrufe in ffmpeg.wasm asynchron, und es wird empfohlen, die Syntax von async/await zu verwenden.

Das Konzept von ffmpeg-core in ffmpeg.wasm ist wie der Motor eines Autos. Es ist nicht nur der wichtigste Teil von ffmpeg.wasm, sondern auch ein austauschbares Bauteil. Derzeit pflegen wir einen Single-Thread-Kern (@ffmpeg/core) und eine Multi-Thread-Version (@ffmpeg/core-mt). Man kann einen eigenen Kern erstellen (z. B. einen Kern nur mit x264-Bibliothek, um die Dateigröße von ffmpeg-core.wasm zu minimieren) mithilfe der Build-Skripte im Repository von ffmpeg.wasm.

ffmpeg.worker lädt den WebAssembly-Code (ffmpeg-core) von CDN herunter und initialisiert ihn in WorkerGlobalScope. Für jede zu verarbeitende Eingabevideodatei müssen wir sie zuerst im Dateisystem von ffmpeg-core platzieren und das Ergebnis auch aus dem Dateisystem von ffmpeg-core lesen, sobald es fertig ist.

Wenn wir eine Multi-Thread-Version von ffmpeg-core verwenden, werden von ffmpeg-core innerhalb von ffmpeg.worker mehr Web Worker erstellt.

Die wichtigsten Schritte bei ffmpeg.wasm

Um ffmpeg.wasm im jeweiligen JavaScript Projekt verwenden zu können, muss das Package @ffmpeg/core per npm oder yarn installiert werden.

npm install @ffmpeg/core
yarn add @ffmpeg/core

Nun kann bereits auf die komplette Funktionalität von ffmpeg.wasm im Projekt zugegriffen werden. Ein kurzes Beispiel dazu, wo eine Videodate „test.avi“ in ein „test.mp4“ mittels JavaScript Code transcodiert wird.

ffmpeg.load()

Ist eine erforderliche API, die zu Beginn aufgerufen werden muss, bevor FFmpeg verwendet wird. Was in dieser API passiert, ist:

  • Download von ffmpeg-core.js vom Remote-Server
  • Instanziierung des wasm-Codes von ffmpeg.wasm.

Abhängig von Ihrer Netzwerkgeschwindigkeit und der Hardware der Host-Maschine kann dieser Vorgang mehrere Minuten dauern.

Dateisystem (FS)

Wenn wir uns die API von ffmpeg.wasm ansehn, finden wir z.B. `ffmpeg.FS()`, um Dateisystemoperationen durchzuführen. (In ffmpeg.wasm verwenden wir MEMFS / Memory File System.) Wir können uns dieses FS wie eine virtuelle Festplatte vorstellen, auf der wir die Eingabedatei ablegen und die Ausgabedatei aus dem ffmpeg.wasm-Befehl ziehen können. Dies ist wesentlich, da wir minimale Änderungen am FFmpeg-Quellcode vornehmen und die größtmögliche Ähnlichkeit zur ursprünglichen Befehlszeilenschnittstelle beibehalten möchten. Einige Befehlsoperationen, die man z.B. verwenden kann:

ffmpeg.FS('writeFile', 'filename', data)

Schreibt eine Datei in das MEMFS als Input für ffmpeg.wasm.

ffmpeg.FS('readFile', 'filename')

List eine Datei aus MEMFS als Output für ffmpeg.wasm.

ffmpeg.wasm ffmpeg.FS('unlink', 'filename')

Löscht eine Datei in MEMFS.

ffmpeg.FS('readdir', '/')

Listet die Dateien innerhalb des angegebenen Pfads.

Die vollständige Doku der File System API findet man auf emscripten.

Web Workers

Wenn wir `ffmpeg.run()` ausführen, werden möglicherweise viele Web Workers erstellt. Dies ist eine normale Situation, da Web Workers Threads in FFmpeg simulieren, was gut ist, da wir nicht möchten, dass ffmpeg.wasm unseren Hauptthread blockiert.

SharedArrayBuffer

Um das Konzept der Web Worker von ffmpeg.wasm umzusetzen, muss der Speicher gemeinsam genutzt werden. Dazu müssen wir SharedArrayBuffer-Objekte von einem Agenten im Cluster zu einem anderen verwenden (ein Agent ist entweder das Hauptprogramm der Webseite oder einer ihrer Webworker).

SharedArrayBuffer ist ein ziemlich neuer Datentyp in JavaScript, der derzeit von den meisten Browsern aus Sicherheitsgründen noch nicht vollständig unterstützt wird. Das ist auch das Problem bei der Verwendung von ffmpeg.was in einer mobilen Umgebung. Um in ffmpeg.wasm pthread / mutli-threading-Unterstützung zu ermöglichen, um die Geschwindigkeit zu erhöhen, ist es ein erforderlicher Datentyp.

Keine vollständige Nutzung der CPU-Fähigkeiten

Die meisten Bibliotheken, die mit ffmpeg arbeiten (z. B. x264), verwenden Assemblersprache wie x86, um den Prozess zu beschleunigen. Aber leider kann WebAssembly diesen x86-Assemblercode nicht direkt verwenden, da er nicht kompatibel ist oder bestimmte Anweisungen nicht unterstützt werden. Es ist noch ein langer Weg, da sich ffmpeg.wasm noch in einem frühen Stadium, obwohl es im Vergleich zur ursprünglichen Version noch sehr langsam ist, aber mit dem Wachstum und der Entwicklung von WebAssembly wird es immer nützlicher werden.

DIY Web Transcoder für lokale Mediendateien

Um die beschrieben Funktionalitäten von ffmpeg.wasm etwas praktischer darzustellen, wurde ein kleiner adaptierbarer Web Transcoder für Mediendateien (Audio & Video), die lokal vorhanden sind, umgesetzt. Der gesamte Encodingprozess erfolgt clientseitig vollständig im Browser und dafür wird der Web Assembly Build von FFmpeg.wasm verwendet. Für das Frontend wurde React, Ant Design 4.x (UI) und Next.js verwendet.

Metadaten

Für das Transcoding können jede Art an Audio- und Videodaten (lokal) geladen werden. Die einzige Beschränkung bezieht sich auf die Dateigröße seitens von Web Assembly von 2 GB.  Metadaten von der eingelesenen Datei werden mittels einem Web Assembly Build dem Medieninfo Tool ffprobe anzeigt. (Funktioniert leider derzeit nur mit Medien mit dem Container .mov & .mp4).

Transcoding

Mit dem Konfigurator kann der gewünschte Export der Mediendatei durchgeführt werden. Dabei stehen folgende Einstellungsmöglichkeiten zur Verfügung.

  • Container der Mediendatei für Audio und Video
  • Video Codec
  • Audio Codec

Nach Auswahl der gewünschten Optionen wird das FFmpeg Kommando generiert (siehe Screenshot), transcodiert und nach erfolgreichem Export zum Download angeboten.(Aufgrund von Einschränkungen seitens ffmpeg.wasm funktioniert das Transcoding derzeit nur bei Videos bis zu einer Auflösung von 1920x1080px).

Das Projekt “DIY Web-Transcoder” kann man hier probieren:

Fazit

Die Unterschiede bei den Transcoding-Zeiten zwischen ffmpeg.wasm und der Standalone-Version von FFmpeg sind derzeit noch enorm groß.

  • FFmpeg in Terminal (Standalone): speed=~34.1x
  • Ffmpeg.wasm im Web Browser: speed=~4.26x (Screenshot darunter)

Derzeit ist außerdem die Verarbeitung von Videos mit einer Auflösung größer als 1920x1080px nicht möglich.

In den kommenden Jahren wird voraussichtlich eine erhebliche Verbesserung erfolgen, da eine große Community an der Entwicklung beteiligt ist.

Trotzdem eignet sich ffmpeg.wasm ideal für eine schnelle und unkomplizierte Umwandlung von Mediendateien, ohne die Notwendigkeit einer aufwendigeren Installation von FFmpeg oder anderer Transcoding-Software. Darüber hinaus ermöglicht es die schnelle Generierung von Transcoding-Presets, um bestimmte Produktionsstandards in einem Unternehmen einzuhalten.

Quellen

Bilder

The comments are closed.