Ich habe am 10. Dezember meinen Vortrag über Rust-Programmierung bei der LUG Jena gehalten. Da es eine Online-Veranstaltung war, haben wir BigBlueButton genutzt und den Vortrag aufgezeichnet. Leider war meine Internetverbindung nicht so gut und die Qualität des Videos ist sehr mangelhaft, aber vielleicht hilft es doch dem einen oder anderen.

Die komplette Veranstaltung mit dem Chatverlauf ist ebenso über den BigBlueButton-Server abrufbar. Das obige Video ist eine Zusammensetzung aus der Bildschirm- und der Videoaufzeichnung (ohne Kamerabild) mit zusätzlichen Kapitelmarken und ohne die ersten acht Minuten voller Störungen.

Nachbearbeitung mit ffmpeg

BigBlueButton erlaubt es leider nicht direkt, die Teile der Aufzeichnung herunterzuladen, weshalb man sich die URLs dafür selbst zusammenstellen muss. Wenn die URL für eine Aufzeichnung https://…/playback/presentation/2.0/playback.html?​meetingId=​KENNUNG lautet, wobei die KENNUNG eine sehr lange Zahl ist, dann lassen sich die Teile der Aufzeichnung über folgende Adressen abrufen:

  • Bildschirm: https://…/presentation/KENNUNG/deskshare/deskshare.webm
  • Kamera und Ton: https://…/presentation/KENNUNG/video/webcams.webm
  • Chatverlauf: https://…/presentation/KENNUNG/slides_new.xml

Da ich die Kamera nicht eingeschaltet hatte, war in dem webcams.webm kein Bild und nur der Ton. In der Bildschirmaufzeichnung war wiederum das Bild und daher wollte ich beide Videos zusammenmischen. Da die ersten acht Minuten voller Störungen waren und ich dann ein zweites Mal begonnen hatte, sollte dieser Anfangsteil entfernt werden.

Ich habe mich erst mit VLC probiert: man kann über Medien, Mehrere Dateien öffnen die Hauptdatei (deskshare.webm) wählen und dann in dem Dialog mehr Optionen anzeigen wählen und dann bei Play another media synchronously das zweite Video angeben. Allerdings habe ich keine Kommandozeile hierfür hinbekommen und bin dann zu ffmpeg gewechselt.

Mit ffmpeg ist das Mischen der Videos einfach: man gibt die Quellen mit der Option -i an, wobei diese von 0 beginnend durchnummeriert werden. Die Option -codec copy gibt an, dass keine Umwandlung stattfinden soll und die Formate beibehalten werden sollen. Mit -map kann man Zuordnungen treffen und da es nur ein Ausgabeziel gibt, kann man dieses weglassen. Mit 1:v wird das Video aus der 2. Quelle und mit 0:a wird der Ton aus der 1. Quelle verwendet. Mit -ss kann man die Startzeit bestimmen, sprich vorspulen.

ffmpeg -i webcams.webm -i deskshare.webm -codec copy -map 1:v -map 0:a \
  -ss 8:18 joined.webm

Kapitelmarken dem Video hinzufügen

Da ich in der Vorbereitung des Vortrags eine Gliederung mit Abschnitten hatte, wollte ich diese auch im Video kennzeichnen. Für ffmpeg gibt es auch ein Format für die Metadaten und mit etwas Bastelei habe ich es auch hinbekommen. Am Anfang der Datei stehen die Metadaten des Videos und danach kommen die Kapitel. Die TIMEBASE ist eine rationale Zahl, die die Grundeinheit für die Zeitstempel angibt. Wer also seine Zeitstempel im 5‑Sekunden-Takt angeben, muss den Wert 5/1 nutzen, wer die Zeitstempel als Zehntel-Sekunden angeben will, muss 1/10 verwenden.

;FFMETADATA1
title=Programmierung in Rust
author=Jörg Sommer <joerg@jo-so.de>
year=2020
date=2020-12-10
copyright=© 2020 Jörg Sommer <joerg@jo-so.de>, CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0/deed.de>
comment=See <https://jo-so.de/2020-12/Rust-Vortrag.html>
encoder=BigBlueButton

[CHAPTER]
; Alle Zeitstempel in ganzen Sekunden
TIMEBASE=1/1
; Beginn bei 0:00 in der Ausgabe
START=0
END=76
TITLE=Problemstellung

[CHAPTER]
TIMEBASE=1/1
; Beginn bei 1:16 in der Ausgabe
START=76
END=145
TITLE=Idee

[CHAPTER]
TIMEBASE=1/1
; Beginn bei 2:25 in der Ausgabe
START=145
END=345
TITLE=Recherche bei crates.io

[CHAPTER]
TIMEBASE=1/1
; Beginn bei 5:45 in der Ausgabe
START=345
END=945
TITLE=Neues Projekt anlegen

[CHAPTER]
TIMEBASE=1/1
; Beginn bei 15:45 in der Ausgabe
START=945
END=1582
TITLE=strsim einbinden

[CHAPTER]
TIMEBASE=1/1
; Beginn bei 26:22 in der Ausgabe
START=1582
END=3342
TITLE=Paare von Wörtern bilden

[CHAPTER]
TIMEBASE=1/1
; Beginn bei 55:42 in der Ausgabe
START=3342
END=4518
TITLE=Fehlerbehandlung

[CHAPTER]
TIMEBASE=1/1
; Beginn bei 1:15:18 in der Ausgabe
START=4518
END=6289
TITLE=Wörter von Stdin lesen

[CHAPTER]
TIMEBASE=1/1
; Beginn bei 1:44:49 in der Ausgabe
START=6289
END=7625
TITLE=Worte von der Kommandozeile übernehmen

[CHAPTER]
TIMEBASE=1/1
; Beginn bei 2:07:05 in der Ausgabe
START=7625
END=8640
TITLE=Kommandozeilenoptionen – Makros

[CHAPTER]
TIMEBASE=1/1
; Beginn bei 2:24:00 in der Ausgabe
START=8640
END=8958
TITLE=Release

[CHAPTER]
TIMEBASE=1/1
; Beginn bei 2:29:18 in der Ausgabe
START=8958
END=9500
TITLE=Ergebnisse

Die Datei mit den Metadaten muss man als 3. Quelle hinzufügen und kann sie über -map_metadata für die Metadaten und -map_chapters für die Kapitelmarken einmischen. Da durch das Vorspulen die Zeitstempel der Kapitelmarken nicht passen, muss man mit -itsoffset den Versatz der Quelle nach der Option festlegen – es funktioniert auch, wenn man die Zeitstempel in der Datei umrechnet.

Am Ende sah der gesamte Befehl wie folgt aus und die Konvertierung lief auch binnen weniger Sekunden.

ffmpeg -i webcams.webm -i deskshare.webm -itsoffset 8:18 -i metadata \
  -codec copy -map 1:v -map 0:a -map_metadata 2 -map_chapters 2 \
  -ss 8:18 joined.webm

Kapitelmarken mit HTML

Da ich das Video oben in die Webseite eingebunden habe, kam mir die Frage, ob die Kapitelmarken nicht auch mit HTML nutzbar sind. Es gibt zwar das Element <track>, um Zusatzinformationen wie Untertitel (mit Übersetzungen; subtitles), Bildbeschreibungen (für Gehörlose; captions) und Kapitelmarken (chapters) einem <video> hinzuzufügen, aber weder im Chromium 83, noch im Firefox 84 werden die Kapitelmarken angezeigt.

Das Format Web Video Text Tracks Format (WebVTT) zur Beschreibung der Kapitel existiert auch schon lange und ist recht einfach. Einen entsprechenden Validator für WebVTT gibt es ebenfalls. Nur fehlt die Unterstützung durch die Browser oder ich finde sie nicht.

WEBVTT

00:00.000 --> 01:16.000
Problemstellung

01:16.000 --> 02:25.000
Idee

02:25.000 --> 05:45.000
Recherche bei crates.io

05:45.000 --> 15:45.000
Neues Projekt anlegen

15:45.000 --> 26:22.000
strsim einbinden

26:22.000 --> 55:42.000
Paare von Wörtern bilden

55:42.000 --> 01:15:18.000
Fehlerbehandlung

01:15:18.000 --> 01:44:49.000
Wörter von Stdin lesen

01:44:49.000 --> 02:07:05.000
Worte von der Kommandozeile übernehmen

02:07:05.000 --> 02:24:00.000
Kommandozeilenoptionen – Makros

02:24:00.000 --> 02:29:18.000
Release

02:29:18.000 --> 02:33:49.000
Ergebnisse

HTML-Videos mit Videojs

Daher muss man leider auf Javascript-Bibliotheken zurückgreifen. Über den Vergleich verschiedener HTML-Player bin ich auf Video.js gestoßen, das sich mit Plugins leicht erweitern und das Aussehen anpassen lässt. (Interessant wäre auch noch MediaElement.js)

Für Video.js gibt es ein Paket bei npm und der Quelltext ist bei GitHub unter der Apache-2.0-Lizenz verfügbar. Nachdem ich es in die Seite eingebunden hatte, wurden auch die Kapitelmarken sofort erkannt und konnten über das kleine Menü neben der Zeit genutzt werden.

In der Liste der Plugins habe ich auch gleich einige Erweiterungen entdeckt, die ich nutzen wollte; unter anderem die Tastatursteuerung über hotkeys, den Knopf zum Speichern mit vjsdownload und Verbesserung für mobile Geräte mobile-ui[^](Bei mobile-ui hat sich dann leider im Nachhinein herausgestellt, dass es fehlerhaft ist.). Die Erweiterungen seek-buttons und chapter-nav sind zwar auch interessant, aber leider mit dem CDN-Google-Fonts umgesetzt, wofür ich mir erst noch einen Ersatz suchen muss.

<video controls preload=none>
  <source src="Rust-Vortrag.webm" type="video/webm" />
  <track kind="chapters" label="Kapitelmarken" src="Rust-Vortrag.vtt" srclang="de">
</video>

<script>
document.body.querySelectorAll('video, audio').forEach(el => {
    el.classList.add('video-js');
    const vid = videojs(el);
    vid.fluid(true);

    vid.ready(function() {
        this.hotkeys({
            enableModifiersForNumbers: false,
            seekStep: ev => {
                // select seek step width depending on modifier keys
                if (ev.shiftKey)
                    return 5;
                if (ev.ctrlKey)
                    return 60;
                return 10;
            },
        });

        this.vjsdownload();
    });
});

Video.js unterstützt auch Tonaufnahmen, sodass ich die Aufnahme meines Gesprächs mit Katja Müller zum Thema Bücherei auch direkt auf der Seite einbinden konnte.

<audio controls preload=none>
  <source src="Bibliothek.mp3" type="audio/mp3" />
</audio>

Ich habe bei weitem hier nur einen kleinen Teil der Möglichkeiten von video.js genutzt, aber es mehr als die Wiedergabe des Browsers und bei Gelegenheit lässt sich es sich auch noch ausbauen. Als Ideen habe ich die Nutzung des Query-Parameters ?t=… bzw. könnte man auch einen Ansprung der Kapitel bauen und die Anpassung der Abspielgeschwindigkeit.