Im folgenden Artikel gehe ich auf die Möglichkeiten und Limitationen von HTML5 und JavaScript für Timecode-Anwendungen ein. Check it out: https://tonmenschen.de/sync/
Besonders in Zeiten von Streaming- und Remote-Produktionen ist Live nicht gleich Live:
Wenn Timing alles ist,
muss Latenz null sein?!
- Vor Ort gibt es Signale, die in Echtzeit verarbeitet und vorliegen müssen: Monitoring / In-Ear, Video-Preview, Kommando, Steuersignale … überall dort hat Latenz nichts zu suchen!
- Doch schon bei der Ausspielung vor Ort verlassen wir die „Null-Zeit“ und haben systembedingten Versatz, z.B. bei Beschallung und Videowalls … hier ist low-latency Key!
- Aber sobald das fertige Programm den TV-Compound verlässt, schleicht sich unweigerlich ein gewisser (oder ungewisser) Versatz ein: Encoding / Decoding, Uplink / Downlink, Buffersize, Multiplexing, ADC / DAC, Packet Loss, Error-Correction, …
Darum die berechtigte Frage: Wenn in der Arena ein Tor fällt, wann fällt es bei uns zuhause?
TV Broadcast vs. Internet Streaming
Bei der klassischen TV-Produktion sind die Zeiten recht gut berechenbar, inklusive Nipplegate-Delay. Das Signal wandert entlang einer vordefinierten Route vom Ü-Wagen zum SNG zum Satelliten zum Schaltraum zum DVB-Stack zum Playoutcenter zum heimischen Flimmerkasten.
Dass ein Signal beim Streaming länger unterwegs ist, merkt man immer dann, wenn der Nachbar schon jubelt!
Doch wie verhält sich das beim Live-Streaming? Vom Ort des Geschehens nimmt das Signal oft zunächst auch eine vordefinierte Route durchs Netz, Multiprotocol Label Switching (MPLS) ist eine feine, sichere und teuere Sache. Aber spätestens bei der Distribution muss man sich fragen: Wieviele und welche Hops nimmt das Signal wohl im „dirty“ Internet?
Welchen Puffer hält uns Youtube beim eintreffenden RTMP-Stream vor? Mit welchem Gerät, welchem OS, welchem Browser sehen sich die Zuschauer das Spektakel denn an?
Deine individuelle Latenz, vom Spielfeld bis zur Couch
Doch wie hoch diese Latenz ist, kann man nicht exakt vorhersagen … aber messen! Genau hier liegt der Ansatz für diese kleine Studie:
- Eine weltweit verfügbare A/V-Quelle mit eindeutigem Zeitstempel on-site einspeisen
- Dieses Signal durch die bestehende Infrastruktur bis zum heimischen Browser streamen
- Dahoam parallel zum Stream, diese ominöse Timestamp-Source auch auf direktem Weg anzapfen
- Beide Signale abgleichen, um seine individuelle Latenz abzulesen, von mir aus einen Screenshot machen oder mit dem Handy beide Fenster nebeneinander knipsen.
Testsignal: Timecode und Synchron-Beep
Aber welche Quelle, welches weltweit verfügbare Signal kann man dafür verwenden?
Wie wäre es mit dem hier: https://tonmenschen.de/sync/
Was ist das jetzt? Ein Timecode, der sich fortlaufend mit einer Atomuhr synchronisiert … naja, fast! Ich habe mir nämlich bei der Entwicklung ein Handicap gegönnt: Client-side Scripting only, Javascript all the way. Das Ganze ist nichts Neues, Services wie www.atomuhr.de gibt es wie Sanduhren am Meer, aber ich brauche etwas mit klarem audiovisuellen zeitlichen Bezug. Quasi ein Live-VALID (Video Audio Lineup & ID) mit Zeitstempel.
Wie spät ist es? NTP o’Clock!
Um sich vernünftig mit einem Time-Server zu synchronisieren, führt eigentlich kein Weg an NTP (network time protocol) vorbei. Auf Port 123 wird die eigene Systemzeit an einen Server geschickt (t0). Dieser empfängt die Anfrage zum Zeitpunkt (t1). Der Time-Server generiert einen Timestamp (t2) und schickt ihn zurück zum anfragenden Rechner. Dieser empfängt das Paket kurze Zeit später (t4).
Mithilfe der vier Zeiten kann der Offset zum Time-Server bestimmt und um die Zeit für den Roundtrip korrigiert werden. Nach und nach nähert sich der Client so der Zeit des Servers an und hält letztlich mit ihr Schritt. Trotz der billigen und ungenauen Quarze in unseren Rechnern und Handys, haben wir dank NTP letztlich doch präzise Zeitmesser vor uns.
Wanted: JSON timestamp
Ich habe allerdings mit VanillaJS keine Möglichkeit gefunden, ohne serverseitige Scripts ein NTP-Protokoll aufzusetzen: UDP und JavaScript verträgt sich einfach nicht. Stattdessen habe ich einen Server gefunden, der einen UNIX timestamp als JSON Object springen lässt: www.worldtimeapi.org – jippie! Dieser lässt sich nämlich wunderbar mit Javascript verzehren und verarbeiten!
Ich frage den JSON timestamp an, mein Zeitpunkt t0:
request.open('GET', 'https://worldtimeapi.org/api/ip', true);
Der Server antwortet mit t2, dem JSON Timestamp, den er sich hoffentlich zuvor von einem Time-Server geholt hat. Zum Zeitpunkt t3 trifft das JSON Objekt bei mir ein:
if (request.readyState == XMLHttpRequest.DONE) { if (request.status == 200) { timeAnswer = Date.now(); ...
Mit den drei Zeiten (T1, T3, T4) und dem guten Glauben daran, dass die Laufzeiten zum und vom Server identisch sind, kann ich eine Genauigkeit errechnen, die für meine Zwecke ausreicht: Irgendwo im Framebereich zwischen 20 – 40 ms:
Proof of Concept: Check!
Links das Handy in Canada (VPN by windscribe), rechts der Laptop in München. Beide machen brav alle 120 Sekunden einen Zeitabgleich und laufen so framegenau synchron.
Nicht alles was tickt, ist eine Golduhr!
Bei all der Freude über Plattformunabhängigkeit und responsive design, muss ich auch ein paar bittere Pillen schlucken:
Der integrierte Audio-Video Sync Test (ping-pong links-rechts oder mono beep) ist Dank dem bekannten HTML5 audio-lag suboptimal bis nutzlos. Im mobilen Browser kommt das Audiosignal auch mal 3-5 Frames zu spät. Auch hier würden serverseitige Audio-Implementierungen helfen … aber mein Handycap sagt: JS only!
Der re-sync alle zwei Minuten klappt wunderbar und verhindert Drift. Leider ist die Zeit für den Roundtrip nicht immer gleich und verschiebt so den Sync im Microtiming – auch wenn ich den Server vor der eigentlichen Anfrage schon mal auf mich einstimme und vorsorglich ein WebSocket öffne.
Für bessere Präzision muss ich auf Kosten der CPU-Last meine TC-Uhr quasi overclocken:
updateIntervall = setInterval(update, 1000 / settingFps / 4)
Weil JavaScript kein Fan von Echtzeit ist, rufe ich vier mal pro Frame meinen Update-Loop auf, um nicht zu spät auf den nächsten Frame zu wechseln.
Wer Lust hat, kann sich gerne bei der Optimierung einklinken: https://github.com/tonmenschen/sync-test
Next step → Serverseitige Unterstützung
Mit client-side JavaScript bin ich durch, als nächstes muss jetzt doch der Server herhalten:
- Echtes NTP implementieren, ein paar Zeilen PHP und das Einbinden von ntpdate sollten den Job machen
- LTC Timecode-Generator integrieren, eventuell hiermit: node-timecodes
- HTML5 Audio Lag loswerden: lowLag.js
Danke fürs Lesen und Ausprobieren … vielleicht ja auch nur, um mal fancy die alte Casio zu stellen.
Sync long and prosper,
Martin