Audio-Wellenform-Videos mit FFmpeg & Node.js erstellen

Es ist kein Geheimnis, dass wir hier bei Transloadit Open-Source-Projekte lieben. Wir haben mehrere ambitionierte Projekte veröffentlicht, etwa Uppy und tus, und unsere Gründer haben sich sogar durch die Mitarbeit an einem Open-Source-Projekt kennengelernt. Wir nutzen im Hintergrund außerdem viele Open-Source-Tools wie FFmpeg, das den Kern unseres Video-Encodings bildet.
FFmpeg (Fast Forward MPEG) ist ein Kommandozeilenprogramm, mit dem sich Audio- und Videodateien transkodieren lassen: zwischen Formaten konvertieren, Sampleraten und Auflösungen ändern und vieles mehr. FFmpeg treibt unsere Services für Video-Encoding und Audio-Encoding an, die jeweils über mehrere Robots verfügen.
Standardmäßig konfiguriert und skaliert Transloadit FFmpeg im Hintergrund und bietet damit eine
leistungsstarke Abstraktion für gängige Anwendungsfälle. Wenn Sie jedoch abseits der ausgetretenen
Pfade arbeiten möchten, können Sie das tun. Die Robots dieser
Kategorien stellen einen Parameter ffmpeg bereit, mit dem Sie direkt mit FFmpeg
interagieren und Funktionen außerhalb unserer Standardfeatures umsetzen können.
Das heutige Ziel
In diesem Beitrag möchten wir mithilfe unseres FFmpeg-Parameters und unseres Node.js-SDK auf kreativere Transcoding-Optionen aufmerksam machen, die Transloadit zu bieten hat. Wir haben bereits Demos, die zeigen, wie sich mit FFmpeg ein Videostream entfernen (English), eine Samplerate ändern (English) und eigene Encoding-Voreinstellungen erstellen (English) lassen. Heute möchten wir zeigen, dass Sie eine Assembly erstellen können, die Audio-Wellenform-Bilder erzeugt, die synchron zu einer eingegebenen Audiospur horizontal scrollen.
Das Rezept dafür schreiben wir als JSON-Dokument. Wir speichern es als Template zur späteren Verwendung.
Legen wir unser Template an. Öffnen Sie in Ihrem Transloadit-Konto den Bereich Templates, klicken Sie auf „Neues Template“ und speichern Sie das folgende JSON darin.
Erstellen Sie zunächst Template-Zugangsdaten für Amazon S3 und ersetzen
Sie unten MY_S3_CREDENTIALS durch deren gespeicherten Namen. Die
S3-Einrichtungsanleitung beschreibt die erforderlichen
Bucket-Berechtigungen. In diesem Beispiel bleiben die exportierten Objekte privat.
{
"steps": {
":original": {
"robot": "/upload/handle"
},
"waveformed": {
"robot": "/audio/waveform",
"use": ":original",
"result": true,
"width": 8000,
"height": 600,
"background_color": "ffffffff",
"outer_color": "1974c7aa",
"center_color": "1974c7aa",
"format": "image"
},
"merged": {
"robot": "/video/merge",
"use": {
"steps": [
{
"name": "waveformed",
"as": "image"
},
{
"name": ":original",
"as": "audio"
}
]
},
"result": true,
"ffmpeg_stack": "v7",
"preset": "ipad-high",
"width": "${file.meta.width}",
"height": "${file.meta.height}"
},
"encode_effect": {
"robot": "/video/encode",
"use": "merged",
"ffmpeg_stack": "v7",
"preset": "empty",
"ffmpeg": {
"vf": "crop=w=800:h=${file.meta.height}:x='(${file.meta.width}-800)*t/${file.meta.duration}':y=0",
"t": "${file.meta.duration}"
}
},
"exported": {
"use": "encode_effect",
"robot": "/s3/store",
"credentials": "MY_S3_CREDENTIALS",
"acl": "bucket-default"
}
}
}
Sehen wir uns nun an, was hier passiert. Unser erster Step, oben
mit dem Namen ":original", nimmt unsere Datei entgegen, die wir in diesem Fall mit dem
Node.js-SDK übermitteln; keine Überraschungen.
In unserem zweiten Step, "waveformed",
erzeugen wir unser Wellenform-Bild. Wir können Optionen wie background_color und
outer_color (Außenfarbe der Wellenform) festlegen. Halten wir es einfach und bleiben bei
einem weißen Hintergrund und einer blauen Wellenform. Bei diesem
Step fällt Ihnen vielleicht auf, dass wir
width für unsere erzeugte Wellenform deutlich größer gesetzt haben, als Sie es
normalerweise erwarten würden. Wir erklären das weiter unten genauer. Wichtig ist vor allem: Je größer
die Breite, desto flüssiger wird der Scroll-Effekt.
Weiter zu unserem dritten Step,
"merged". Wie der Step-Name schon
andeutet, führen wir die neu erzeugten Wellenform-Bilder und unsere ursprüngliche Audiodatei zusammen, um
eine neue mp4-Videodatei zu erzeugen. Verwenden Sie die Namenskonvention
"name", "as" im
Step use, und schon sind Sie
startklar. Anschließend verwenden wir Assembly Variables, um die
Parameterwerte width und height auf dieselben
Abmessungen wie unser Eingabebild zu setzen.
Nun zum spannenden Teil. In unserem Step
"encode_effect" wird es etwas ungewöhnlich. Hier leiten wir unsere vollständig
zusammengeführte Wellenform-Videodatei an einen Robot
/video/encode weiter. Mit dem FFmpeg-Parameter können wir direkt mit dem
Kommandozeilenprogramm FFmpeg arbeiten. Für unseren Videoeffekt verwenden wir das Flag
"vf". Sehen wir uns das genauer an!
"crop=w=800:h=${file.meta.height}:x='(${file.meta.width}-800)*t/${file.meta.duration}':y=0"
Wir verwenden hier den Filter crop, um die Größe unseres Endergebnisses
anzupassen. In unserem Step waveformed
haben wir die Gesamtbreite auf 8000 gesetzt. Wir wollten beim Scrollen nur 10 % des gesamten Bildes
zeigen, deshalb geben wir 800 als Zielbreite an. Auch hier nutzen wir eine der
Assembly Variables, um die Zielhöhe auf die Höhe der eingegebenen Datei
zu setzen. Da wir uns nur von links nach rechts bewegen, setzen wir y auf
0. Für x steuert unser Ausdruck die horizontale
Position des Crop-Fensters und sorgt dafür, dass unser Video am linken Bildrand beginnt. Mithilfe von
t ermitteln wir, wie lange das zugeschnittene Bild bis zum
gegenüberliegenden Bildrand benötigt. Dieser Wert ist für jede Audiodatei, die wir unserer
Assembly übergeben, anders. Deshalb verwenden wir erneut eine
Assembly Variable, damit wir den korrekten Zeitwert zuverlässig aus den
Metadaten unserer Eingabedatei erhalten.
Doch das ist nicht das einzige Flag, das wir in unserem FFmpeg-Parameter verwenden. Zusätzlich nutzen
wir das Flag t. Es legt lediglich die Länge der resultierenden Datei fest.
Sie werden bemerken, dass wir für dieses Flag denselben Wert verwenden wie oben beim Flag
vf.
Zum Schluss exportieren wir unser Ergebnis mit dem Robot /s3/store, und damit sind wir fertig!
Erste Schritte
Jetzt, da wir unser Template haben, richten wir unsere SDK-Integration ein. Für diese Demonstration haben wir uns für Node.js entschieden, aber das ließe sich leicht auf unsere anderen verfügbaren SDKs übertragen. Bevor wir beginnen, müssen wir jedoch Transloadits Node.js-SDK installieren. Führen Sie nach der Initialisierung von npm in Ihrem Projektordner den folgenden Befehl aus.
npm i transloadit@4 --save
Verwenden Sie Node.js 20 oder neuer und speichern Sie das vollständige Programm als
waveform-video.mjs, damit Node es als ES-Modul lädt. Führen Sie es mit
node waveform-video.mjs aus, nachdem Sie die unten genannten Zugangsdaten gesetzt haben.
Im Folgenden haben wir unseren Code in eine Reihe von Steps aufgeteilt. Damit Sie nicht jeden Step mühsam kopieren müssen, gibt es am Ende eine Version zum Kopieren und Einfügen.
Zuerst importieren wir die Transloadit-Bibliothek.
import { Transloadit } from 'transloadit'
Als Nächstes richten wir unseren Transloadit-Client ein. Setzen Sie TRANSLOADIT_AUTH_KEY und
TRANSLOADIT_SECRET_KEY in Ihrer Serverumgebung auf den Auth Key und das Auth Secret aus Ihrem
Workspace. Dabei handelt es sich nicht um Template-Zugangsdaten für Storage. Führen Sie diesen Code auf
Ihrem eigenen Rechner oder einem vertrauenswürdigen Server aus, niemals im Browser.
const transloadit = new Transloadit({
authKey: process.env.TRANSLOADIT_AUTH_KEY,
authSecret: process.env.TRANSLOADIT_SECRET_KEY,
})
Nun definieren wir die Audiodatei, die wir in unserer
Assembly verwenden möchten. Legen Sie eine beliebige Audiodatei
im Arbeitsverzeichnis ab und deklarieren Sie zugleich die beiden Variablen
filePath und fieldName. Diese übergeben wir an
createAssembly in dessen Option files.
const filePath = './joakim_karud-rock_angel.mp3'
const fieldName = 'my_file'
Von hier aus erstellen wir unsere Assembly mit der gespeicherten
Template ID. Das SDK gibt ein Promise zurück; waitForCompletion wartet auf die Verarbeitung,
nicht nur auf den Upload. Der folgende Ausschnitt gehört in eine async-Funktion, wie im vollständigen
Programm unten gezeigt.
const opts = {
files: { [fieldName]: filePath },
waitForCompletion: true,
params: {
template_id: 'YOUR_TEMPLATE_ID',
},
}
const result = await transloadit.createAssembly(opts)
console.log(result)
Nachdem unser gesamter Code zusammengestellt ist, können wir unser Programm ausführen. Hier noch einmal das vollständige Programm:
import { Transloadit } from 'transloadit'
async function main() {
const transloadit = new Transloadit({
authKey: process.env.TRANSLOADIT_AUTH_KEY,
authSecret: process.env.TRANSLOADIT_SECRET_KEY,
})
const filePath = './joakim_karud-rock_angel.mp3'
const fieldName = 'my_file'
const result = await transloadit.createAssembly({
files: { [fieldName]: filePath },
waitForCompletion: true,
params: { template_id: 'YOUR_TEMPLATE_ID' },
})
console.log(result)
}
main().catch(() => {
console.error('Waveform video generation failed.')
process.exitCode = 1
})
Als Audiodatei haben wir uns für den Track „Joakim karud-rock angel“ entschieden, den wir häufig in vielen unserer Demos (English) verwendet haben. Wenn Sie dieselbe Datei nutzen möchten, finden Sie sie im GitHub-Repo unten.
Sobald unser Programm durchgelaufen und abgeschlossen ist, können wir uns bei S3 anmelden und unser Endergebnis ansehen.
Wie Sie sehen, ist alles nach Plan verlaufen. Doch jetzt wird es noch interessanter. Wie Sie sehen, sind die Möglichkeiten Ihrer Assemblies mit FFmpeg praktisch grenzenlos. Warum lassen Sie die Wellenform nicht einmal vertikal scrollen oder transkodieren das Video so, dass es rückwärts abgespielt wird?
Oder nutzen Sie den Parameter ffmpeg, um
völlig andere Filter anzuwenden.
Wir hoffen, dieser Beitrag konnte Ihnen zeigen, wie vielseitig die Transloadit API sein kann und wie
nützlich der Parameter ffmpeg bei einer ganzen Reihe kreativer und
unterhaltsamer Projekte ist.
Wenn Sie eines der in diesem Projekt verwendeten Assets herunterladen möchten, besuchen Sie das folgende GitHub-Repo!