Erfahre, wie du mit dem requestVideoFrameCallback()
effizienter mit Videos im Browser arbeiten kannst.
Mit der Methode HTMLVideoElement.requestVideoFrameCallback()
können Webautoren einen Callback registrieren, der in den Renderingschritten ausgeführt wird, wenn ein neuer Videoframe an den Compositor gesendet wird.
So können Entwickler effiziente Operationen auf Video-Frames für Videos ausführen, z. B. die Videoverarbeitung und das Painting auf einem Canvas, eine Videoanalyse oder die Synchronisierung mit externen Audioquellen.
Unterschied zu requestAnimationFrame()
Über diese API ausgeführte Vorgänge wie das Zeichnen eines Videoframes auf einem Canvas mithilfe von drawImage()
werden bestmöglich mit der Framerate des auf dem Bildschirm wiedergegebenen Videos synchronisiert.
Im Unterschied zu window.requestAnimationFrame()
, das normalerweise etwa 60 Mal pro Sekunde ausgelöst wird, ist requestVideoFrameCallback()
an die tatsächliche Framerate des Videos gebunden – mit einer wichtigen Ausnahme:
Die effektive Rate, mit der Callbacks ausgeführt werden, ist die geringere Rate zwischen der Rate des Videos und der Rate des Browsers. Das bedeutet, dass ein Video mit 25 fps, das in einem Browser mit 60 Hz malt, Callbacks mit 25 Hz auslösen. Ein Video mit 120 fps im selben 60-Hz-Browser würde Callbacks mit 60 Hz auslösen.
Tipps zur Benennung von Sitemaps
Aufgrund der Ähnlichkeit mit window.requestAnimationFrame()
wurde die Methode anfangs als video.requestAnimationFrame()
vorgeschlagen und in requestVideoFrameCallback()
umbenannt. Dies wurde nach einer langen Diskussion vereinbart.
Funktionserkennung
if ('requestVideoFrameCallback' in HTMLVideoElement.prototype) {
// The API is supported!
}
Unterstützte Browser
Polyfill
Es ist ein Polyfill für die Methode requestVideoFrameCallback()
verfügbar, die auf Window.requestAnimationFrame()
und HTMLVideoElement.getVideoPlaybackQuality()
basiert. Beachten Sie vor der Verwendung die Einschränkungen in der README
.
Methode „requestVideoFrameCallback()“ verwenden
Wenn Sie schon einmal die requestAnimationFrame()
-Methode verwendet haben, werden Sie sich sofort mit der requestVideoFrameCallback()
-Methode vertraut machen.
Sie registrieren einen ersten Callback einmal und werden jedes Mal neu registriert, wenn der Callback ausgelöst wird.
const doSomethingWithTheFrame = (now, metadata) => {
// Do something with the frame.
console.log(now, metadata);
// Re-register the callback to be notified about the next frame.
video.requestVideoFrameCallback(doSomethingWithTheFrame);
};
// Initially register the callback to be notified about the first frame.
video.requestVideoFrameCallback(doSomethingWithTheFrame);
Im Callback ist now
ein DOMHighResTimeStamp
und metadata
ein VideoFrameMetadata
-Wörterbuch mit den folgenden Attributen:
presentationTime
vom TypDOMHighResTimeStamp
: Der Zeitpunkt, zu dem der User-Agent den Frame zur Komposition übermittelt hat.expectedDisplayTime
vom TypDOMHighResTimeStamp
: Die Zeit, zu der der User-Agent erwartet, dass der Frame sichtbar ist.width
vom Typunsigned long
: Die Breite des Videoframes in Medienpixeln.height
vom Typunsigned long
: die Höhe des Videoframes in Medienpixeln.mediaTime
vom Typdouble
: Der Zeitstempel der Medienpräsentation (PTS) in Sekunden des dargestellten Frames (z.B. der Zeitstempel auf dervideo.currentTime
-Zeitachse).presentedFrames
vom Typunsigned long
: Die Anzahl der Frames, die zur Komposition gesendet wurden. Ermöglicht Clients festzustellen, ob Frames zwischen Instanzen vonVideoFrameRequestCallback
verpasst wurden.processingDuration
vom Typdouble
: Die verstrichene Zeit in Sekunden ab der Übertragung des codierten Pakets mit demselben Präsentationszeitstempel (PTS) wie dieser Frame (z.B. mit demmediaTime
) an den Decodierer, bis der decodierte Frame für die Präsentation bereit war.
Für WebRTC-Anwendungen können zusätzliche Eigenschaften angezeigt werden:
captureTime
vom TypDOMHighResTimeStamp
: Bei Videoframes, die aus einer lokalen oder Remote-Quelle stammen, ist dies die Zeit, zu der der Frame von der Kamera aufgenommen wurde. Bei einer Remote-Quelle wird die Erfassungszeit mithilfe der Uhrsynchronisierung und RTCP-Absenderberichte geschätzt, um RTP-Zeitstempel in die Uhrzeit zu konvertieren.receiveTime
vom TypDOMHighResTimeStamp
: Bei Videoframes, die aus einer Remote-Quelle stammen, ist dies die Zeit, zu der der codierte Frame von der Plattform empfangen wurde, d. h. der Zeitpunkt, zu dem das letzte Paket, das zu diesem Frame gehört, über das Netzwerk empfangen wurde.rtpTimestamp
vom Typunsigned long
: Der RTP-Zeitstempel, der diesem Videoframe zugeordnet ist.
Von besonderem Interesse auf dieser Liste ist mediaTime
.
Die Implementierung von Chromium verwendet den Audiouhr als Zeitquelle für das video.currentTime
, während mediaTime
direkt durch die presentationTimestamp
des Frames gefüllt wird.
Die mediaTime
sollten Sie verwenden, wenn Sie Frames auf reproduzierbare Weise genau identifizieren möchten, z. B. um genau zu ermitteln, welche Frames Sie verpasst haben.
Wenn alles einen Frame zu weit weg scheint...
Die vertikale Synchronisierung (oder einfach vsync) ist eine Grafiktechnologie, die die Framerate eines Videos und die Aktualisierungsrate eines Monitors synchronisiert.
Da requestVideoFrameCallback()
im Hauptthread ausgeführt wird, der Video-Compositing aber im Hintergrund im Compositor-Thread stattfindet, ist mit dieser API alles möglich und der Browser bietet keine strengen Garantien.
Das kann daran liegen, dass die API eine vsync-verspätete Ausführung aufweist, wenn ein Videoframe gerendert wurde.
Es ist eine Vsync erforderlich, bis Änderungen, die über die API an der Webseite vorgenommen wurden, auf dem Bildschirm angezeigt werden (wie bei window.requestAnimationFrame()
).
Wenn Sie also mediaTime
oder die Frame-Nummer auf Ihrer Webseite mit den nummerierten Videoframes vergleichen, sieht das Video so aus, als wäre es einen Frame voraus.
Tatsächlich ist der Frame bei vsync x bereit, der Callback wird ausgelöst und der Frame wird bei vsync x+1 gerendert. Änderungen im Callback werden bei vsync x+2 gerendert.
Sie können prüfen, ob der Callback über eine vsync-Funktion verzögert ist (und der Frame bereits auf dem Bildschirm gerendert wurde). Dazu prüfen Sie, ob der metadata.expectedDisplayTime
-Wert ungefähr now
oder in der Zukunft eine vsync ist.
Wenn der Abstand innerhalb von fünf bis zehn Mikrosekunden von now
liegt, ist der Frame bereits gerendert. Wenn expectedDisplayTime
etwa sechzehn Millisekunden in der Zukunft liegt (wenn Ihr Browser bzw. Bildschirm mit 60 Hz aktualisiert wird), sind Sie mit dem Frame synchronisiert.
Demo
Ich habe eine kleine Demo zu Glitch erstellt, die zeigt, wie Frames mit genau der Framerate des Videos auf einem Canvas gezeichnet werden und wo die Frame-Metadaten zu Debugging-Zwecken protokolliert werden.
let paintCount = 0;
let startTime = 0.0;
const updateCanvas = (now, metadata) => {
if (startTime === 0.0) {
startTime = now;
}
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
const elapsed = (now - startTime) / 1000.0;
const fps = (++paintCount / elapsed).toFixed(3);
fpsInfo.innerText = `video fps: ${fps}`;
metadataInfo.innerText = JSON.stringify(metadata, null, 2);
video.requestVideoFrameCallback(updateCanvas);
};
video.requestVideoFrameCallback(updateCanvas);
Ergebnisse
Es wird schon sehr lange eine Verarbeitung auf Frameebene durchgeführt – ohne Zugriff auf die tatsächlichen Frames zu haben, sondern nur auf Grundlage von video.currentTime
.
Die requestVideoFrameCallback()
-Methode bietet eine deutliche Verbesserung dieser Problemumgehung.
Danksagungen
Die requestVideoFrameCallback
API wurde von Thomas Guilbert angegeben und implementiert.
Dieser Beitrag wurde von Joe Medley und Kayce Basques geprüft.
Hero-Image von Denise Jans auf Unsplash.