Zum Inhalt springen

Hochwertige Aufzeichnung von Vorträgen/ Zusammenfügen der Videostreams

Aus Wikibooks

Zusammenfügen der Videostreams

[Bearbeiten]

Der »filter_complex« Parameter

[Bearbeiten]

An dieser Stelle kommt ein weiteres mächtiges ffmpeg-Werkzeug zum Einsatz: -filter_complex Dieses ermöglicht fast alle erdenklichen Formatierungen und Modifizierungen von Video- aber auch Audio-Daten. Die zu benutzenden Filter werden in einem sogenannten Filtergraph zusammengefasst. Der von ffmpeg benutzte begriff -graph ist dabei etwas irreführend, und ist letztendlich nichts anderes als eine Aneinanderreihung oder Verknüpfung mehrerer Filterparameter.

Veranschaulichung eines ffmpeg-Filtergraphen am Beispiel des Zusammenfügens dreier Video-Streams.

Filter haben meist einen Input und einen Output. Diese werden in eckigen Klammern spezifiziert. Mehrere Filter im Filtergraph werden durch ein Semikolon getrennt Allgemeiner Syntax:

-filter_complex "...; [ Inputstream ] Filterbezeichnung = Parameter [ Outputstream ]; ..."

Beispiel: Der Skalierungsfilter, mit dessen Hilfe kann man Videostreams vergrößern, verkleinern, stauchen und strecken. Im folgenden wird ein Videostream auf 640x360 Pixel skaliert

-filter_complex "[ Inputstream ] scale=640:360 [ Outputstream ]"

Benötigte Filter und vordefinierte Variablen

[Bearbeiten]

Die Liste der ffmprg-Filter ist lang, nur einige wenige sind für die Realisierung des Stream-Layouts nötig und werden im folgenden aufgelistet und kurz beschrieben.

Filter Erklärung Beispiel
scale vergrößert/verkleinert den angegebenen Video-Stream anhand der übergebenen Breite/Höhe [stream]scale=400:300[skaledstream]
pad füllt einen Bereich aus und definiert ihn z.B. als Hintergrund-Ebene [stream]pad=1920:1080:0:0[paddedstream]
overlay legt einen Stream über einen anderen; die Position wird durch den Abstand von links-oben festgelegt [stream1][stream2]overlay=50:50[overlayedstreams]

Des Weiteren gibt es integrierte Variablen, die nützliche Werte enthalten. Für die Stream-Anordnung sind zwei davon hilfreich und kommen im pad-Filter zum Einsatz.

  • iw (image width): enthält die Breite des aktuellen Streams in Pixeln
  • ih (image height): enthält die Höhe des aktuellen Streams in Pixeln

Verwendung im pad-Filter: [stream]pad=iw:ih:0:0[paddedstream]

Spezifizierung der Streams im Filtergraph

[Bearbeiten]

Da ffmpeg mehrere Inputs durch den -i Parameter aufnehmen kann, liegt es nahe, dass diese eine eindeutige Bezeichnung erhalten müssen, wenn man diese durch einen Filter bearbeiten möchte. Dazu werden die Inputs in folgender Weise indexiert, bei Null beginnend:

[ Input-Nr : Stream-Nr]

Videodateien beinhalten meistens zwei Streams: einen Videostream und einen Audiostream. Diese kann man wie folgt referenzieren:

[0:0] Videostream
[0:1] Audiostream

So würde das obige Beispiel mit dem Skalierungsfilter nun aussehen:

-filter_complex "[0:0] scale=640:360 [scaled]"

Der skalierte Videostream wird nun mit dem Bezeichner scaled referenziert. Dieser Bezeichner kann beliebig alphabetisch benannt werden, er stellt also eine Art Variable für den Stream dar und kann als Input für einen weiteren Filter dienen.

Mit folgendem Filtergraph wird der Videostream also zuerst skaliert und dann um 90 im Uhrzeigersinn gedreht:

-filter_complex "[0:0] scale=640:360 [scaled]; 
                 [scaled] rotate=90*PI/180 [rotated]"

Hat man nun mehrere Inputs in ffmpeg angegeben, kommt die Inputnummer zum Einsatz. Wie man sieht ergibt sich die Indexierung nach der Reihenfolge der -i Parameter:

-i foo.avi
-i bar.avi
-i qux.avi
...
-filter_complex "[0:0]foo-Videostream-Filter[bezeichner1]; 
                 [1:0]bar-Videostream-Filter[bezeichner2]; 
                 [2:0]qux-Videostream-Filter[bezeichner3]"
...

Zwei Videostreams »Seite-an-Seite«

[Bearbeiten]
Überlegungen zum overlay-Filter

Die typische Anordnung der Videostreams bei aufgezeichneten Vorträgen ist der Vortragende im kleineren Ausschnitt neben den Vortragsfolien im größeren Bereich. Diese Anordnung lässt sich mit dem overlay-Filter von ffmpeg erzeugen. Wie der Name schon sagt, wird ein Videostream über einen anderen gelegt – doch in diesem Fall sollen die Streams nebeneinander platziert werden.

Doch was passiert mit dem leeren Bereich, der nicht durch die zwei Streams bedeckt ist? Dieser wird einfach durch ein Hintergrundbild ausgefüllt.

Eine Bilddatei als Video-Hintergrund

[Bearbeiten]

Man kann sich das Hintergrundbild als eine Arbeitsfläche vorstellen, auf der die Video-Streams beliebig angeordnet werden können. Falls bei dieser Anordnung Zwischenräume entstehen, füllt das Hintergrundbild diese aus. Mit dem Einfügen eines solche Hintergrunds ergeben sich zwei weitere positive Effekte:

  • die Bilddatei legt die Auflösung des Videos fest
  • es können Metainformationen auf dem Bild Platz finden und dekorative Elemente in das Video integriert werden, welche über die gesamte Aufnahmedauer angezeigt werden

Das Bild sollte also groß genug sein, damit beide Video-Streams in gewünschter Größe Platz darauf finden und eine standardisierte Videoauflösung besitzen, bspw. 1920x1080. Dieses Befehlsfragment liefert einen Video-Hintergrund aus einer Bilddatei:

-loop 1 -i /pfad/zum/hintergrundbild.png -r 25
Parameter Erklärung
-loop 1 der nach diesem Parameter folgende Input wird in Schleife abgespielt, so kann ein einzelnes Bild zum Videostream werden
-i bild.png Pfad zur Bilddatei; es wird eine Vielzahl von gängigen Formaten unterstützt
-r 25 Bildwiederholung pro Sekunde; 25 ist ausreichend

Demzufolge sind nun drei Video-Streams zu verarbeiten:

  • Kamera
  • Präsentation
  • Hintergrundbild

Das Layout kann mit dem Filter-Parametern von ffmpeg bewerkstelligt werden.

Komposition des Gesamtbildes

[Bearbeiten]

Nun kommt der overlay-Filter zum Einsatz: Er nimmt zwei Videostreams auf und gibt einen, den zusammengefügten, aus. Vorher sollten die Videostreams natürlich noch skaliert und ein paar einfache Berechnung bzgl. der Auflösung des Video-Streams angestellt werden.

An einem konkreten Beispiel soll dieser Zusammenbau erklärt werden:

  • 3 Video-Streams in der Auflösung 1920×1080: Kamera, Präsentation, Hintergrundbild
    • Kamera-Stream: auf ein viertel der Ausgangsgröße skalieren und links platzieren
    • Präsentations-Stream: nimmt den Rest der Video-Breite ein und wird rechts platziert
  • Kamera- und Präsentations-Stream sollen zum Rand und zueinander einen Abstand von 4 Pixeln haben

Glücklicherweise braucht der Skalierungs-Filter nur entweder die Breite oder die Höhe auf die skaliert werden soll. Um das Seitenverhältnis beizubehalten, gibt man für den jeweils anderen Wert einfach eine -1 an. Man sollte nur mit ganzzahligen Werten arbeiten, denn Pixel lassen sich bekanntlich nicht teilen. Ein schmaler Rahmen um die Videostreams ist empfehlenswert, deshalb ziehe man von jeder berechneten Breite noch 6 Pixel ab. Somit hat man später links, mittig und rechts der Streams Platz für einen Rahmen von 4 Pixel. Mit obigen Überlegungen kann man die Breiten der einzelnen Streams wie folgt berechnen.

Breite der jeweiligen Streams:

  • Kamera-Stream: 1920 / 4 = 640
  • Präsentations-Stream: 1920 - 640 = 1280

Die Hälfte der gesamten Rahmenbreite von den 2 berechneten Stream-Breiten abziehen:

  • Subrahend für die jeweiligen Streams: 4 Pixel × 3 / 2 = 6
  • Differenz des Kamera-Streams: 640 - 6 = 634
  • Differenz des Präsentations-Streams: 1280 - 6 = 1274

Die berechneten Werte werden nun für die Filter benutzt. Zur besseren Übersicht sind die einzelnen Filter in einer Tabelle dargestellt.

Parameter Erklärung
[0:0]pad=iw:ih:0:0[bg]; pad legt den Bereich auf dem Input fest, hier also auf den gesamten Bereich
[1:0]scale=634:-1[cam]; skaliert den Inputstream auf 634-Pixel Breite, die Höhenangabe -1 sorgt für die Berechnung der Höhe entsprechend dem Seitenverhältnis.
[2:0]scale=1274:-1[dvi]; gleicher Filter, andere Werte
[bg][cam]overlay=4:4[bgcam]; auf den [bg]-Stream wird der [cam]-Stream (Kamera) gelegt, anzugeben ist hier der Abstand x:y von oben-links bzgl. des zuvor festgelegten pad-Bereiches
[bgcam][dvi]overlay=642:4[bgcamdvi]; auf den zuvor erzeugten [bgcam]-Stream wird der [dvi]-Stream (Präsentation) gelegt; auch hier wird der Abstand vom pad-Bereich angegeben: x = 4 + 634 + 4 = 642 und y = 4

Kompletter ffmpeg-Aufruf

[Bearbeiten]

Im folgenden sind die zahlreichen Parameter zusammengeführt. Parameter aus anderen Kapiteln sind auskommentiert.

ffmpeg 
-loop 1 -i /pfad/zu/hintergrundbild.png -r 25
# -i <(/pfad/zu/bmdcapture -m 10 -V 3 -C 0 -v -F nut -f pipe:foo &) #Kamerastream
# -i <(/pfad/zu/bmdcapture -m 12 -V 3 -C 0 -v -F nut -f pipe:bar &) #Beamerstream
# -f alsa -i hw:0
# -c:v libx264 -preset veryfast
# -c:a ac3
-filter_complex "[0:0]pad=iw:ih:0:0[bg];
                 [1:0]scale=634:-1[cam];
                 [2:0]scale=1274:-1[dvi];
                 [bg][cam]overlay=4:4[bgcam];
                 [bgcam][dvi]overlay=645:4[bgcamdvi]"
#-map "[bgcamdvi]" -map a:0
#outputfile.mkv

Zwei Videostreams Bild-in-Bild

[Bearbeiten]

Diese Darstellung unterscheidet sich nicht – was die verwendeten Filter angeht – von der Seite-an-Seite-Version. Man kann allerdings das Hintergrundbild einsparen, da hier einer der beiden Videostreams über den anderen gelegt wird. Ansonsten sind auch hier nur die gewünschten Positionen und die Skalierungen zu berechnen. Dies dürfte aber aus den vorangegangenen Beispiel klar sein.

Beispiel: Der skalierte Kamerastream liegt links unten über Beamerstream in Originalgröße. ToDo: Bild

ffmpeg 
# -i <(/pfad/zu/bmdcapture -m 10 -V 3 -C 0 -v -F nut -f pipe:foo &) #Kamerastream
# -i <(/pfad/zu/bmdcapture -m 12 -V 3 -C 0 -v -F nut -f pipe:bar &) #Beamerstream
# -f alsa -i hw:0
# -c:v libx264 -preset veryfast
# -c:a ac3
-filter_complex "[0:0]scale=-1:240[cam];
                 [1:0]pad=iw:ih:0:0[dvi];
                 [dvi][cam]overlay=0:ih-240[dvicam];" # von inputhöhe ih wird die höhe des skalierten Kamerastreams abgezogen
-map "[dvicam]" -map a:0
#outputfile.mkv