requestVideoFrameCallback()
verwenden, um effizienter mit Videos im Browser zu arbeiten
Mit der Methode HTMLVideoElement.requestVideoFrameCallback()
können Webautoren einen Callback registrieren, der in den Rendering-Schritten ausgeführt wird, wenn ein neuer Videoframes an den Compositor gesendet wird.
So können Entwickler effiziente Vorgänge pro Videobild ausführen, z. B. Videoverarbeitung und Rendern auf einem Canvas, Videoanalyse oder Synchronisierung mit externen Audioquellen.
Unterschied zu requestAnimationFrame()
Vorgänge wie das Zeichnen eines Videoframes auf einen Canvas mithilfe von drawImage()
, die über diese API ausgeführt werden, werden nach Möglichkeit mit der Framerate des auf dem Bildschirm wiedergegebenen Videos synchronisiert.
Im Gegensatz zu window.requestAnimationFrame()
>, das in der Regel etwa 60-mal pro Sekunde ausgelöst wird, ist requestVideoFrameCallback()
an die tatsächliche Videobildrate gebunden. Es gibt jedoch eine wichtige Ausnahme:
Die effektive Rate, mit der Callbacks ausgeführt werden, ist die niedrigere Rate zwischen der Rate des Videos und der des Browsers. Das bedeutet, dass bei einem Video mit 25 fps, das in einem Browser wiedergegeben wird, der mit 60 Hz gerendert wird, Rückrufe mit 25 Hz ausgelöst werden. Bei einem 120‑fps-Video in diesem 60‑Hz-Browser werden die Callbacks mit 60 Hz ausgelöst.
Tipps zur Benennung von Sitemaps
Aufgrund der Ähnlichkeit mit window.requestAnimationFrame()
wurde die Methode ursprünglich als video.requestAnimationFrame()
vorgeschlagen und in requestVideoFrameCallback()
umbenannt, was nach einer langen Diskussion vereinbart wurde.
Funktionserkennung
if ('requestVideoFrameCallback' in HTMLVideoElement.prototype) {
// The API is supported!
}
Unterstützte Browser
Polyfill
Ein Polyfill für die Methode requestVideoFrameCallback()
basierend auf Window.requestAnimationFrame()
und HTMLVideoElement.getVideoPlaybackQuality()
ist verfügbar. Beachten Sie vor der Verwendung die im README
genannten Einschränkungen.
requestVideoFrameCallback()-Methode verwenden
Wenn Sie die Methode requestAnimationFrame()
schon einmal verwendet haben, werden Sie sich mit der Methode requestVideoFrameCallback()
sofort vertraut fühlen.
Sie registrieren einen ersten Callback einmal und registrieren ihn dann immer wieder neu, wenn er 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 Eigenschaften:
presentationTime
vom TypDOMHighResTimeStamp
: Die Uhrzeit, zu der der User-Agent den Frame für die Komposition gesendet hat.expectedDisplayTime
vom TypDOMHighResTimeStamp
: Der Zeitpunkt, zu dem der User-Agent erwartet, dass der Frame sichtbar ist.width
, vom Typunsigned long
: Die Breite des Videobilds in Media-Pixeln.height
, vom Typunsigned long
: Die Höhe des Videobilds in Media-Pixeln.mediaTime
, vom Typdouble
: Der Zeitstempel der Medienpräsentation (PTS) in Sekunden des präsentierten Frames (z.B. sein Zeitstempel auf dervideo.currentTime
-Zeitachse).presentedFrames
vom Typunsigned long
: Die Anzahl der Frames, die für die Komposition eingereicht wurden. Ermöglicht es Clients, festzustellen, ob zwischen den Instanzen vonVideoFrameRequestCallback
Frames fehlen.processingDuration
, vom Typdouble
: Die verstrichene Dauer in Sekunden von der Übermittlung des codierten Pakets mit demselben Präsentationszeitstempel (Presentation Timestamp, PTS) wie dieser Frame (z.B. derselbe wiemediaTime
) bis zum Decoder, bis der decodierte Frame für die Präsentation bereit war.
Für WebRTC-Anwendungen können zusätzliche Attribute angezeigt werden:
captureTime
vom TypDOMHighResTimeStamp
: Bei Videoframes, die von einer lokalen oder Remote-Quelle stammen, ist dies der Zeitpunkt, zu dem der Frame von der Kamera aufgenommen wurde. Bei einer Remote-Quelle wird die Aufnahmezeit mithilfe der Uhrzeitsynchronisierung und der RTCP-Senderberichte geschätzt, um RTP-Zeitstempel in Aufnahmezeit umzuwandeln.receiveTime
, vom TypDOMHighResTimeStamp
: Bei Videoframes, die von einer Remotequelle stammen, ist dies der Zeitpunkt, zu dem der codierte Frame von der Plattform empfangen wurde. Das heißt, 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 Videobild zugeordnet ist.
Von besonderem Interesse in dieser Liste ist mediaTime
.
In der Chromium-Implementierung wird die Audio-Clock als Zeitquelle für video.currentTime
verwendet, während mediaTime
direkt mit dem presentationTimestamp
des Frames gefüllt wird.
mediaTime
ist die richtige Option, wenn Sie Frames reproduzierbar identifizieren möchten, auch um genau festzustellen, welche Frames Sie verpasst haben.
Wenn die Synchronisierung um einen Frame verschoben ist…
Die vertikale Synchronisierung (oder kurz VSync) ist eine Grafiktechnologie, die die Framerate eines Videos und die Aktualisierungsrate eines Monitors synchronisiert.
Da requestVideoFrameCallback()
im Hauptthread ausgeführt wird, die Videokomposition aber im Compositor-Thread erfolgt, ist alles aus dieser API ein Best-Effort-Ansatz und der Browser bietet keine strengen Garantien.
Möglicherweise ist die API im Vergleich zum Rendern eines Videoframes um einen VSync-Zyklus verzögert.
Es dauert einen VSync, bis Änderungen, die über die API an der Webseite vorgenommen wurden, auf dem Bildschirm angezeigt werden (wie bei window.requestAnimationFrame()
). Wenn Sie also die mediaTime
oder die Framenummer auf Ihrer Webseite ständig aktualisieren und mit den nummerierten Videoframes vergleichen, sieht das Video irgendwann so aus, als wäre es einen Frame voraus.
Tatsächlich ist es so, dass der Frame bei VSync x fertig ist, der Callback ausgelöst und der Frame bei VSync x+1 gerendert wird und Änderungen, die im Callback vorgenommen werden, bei VSync x+2 gerendert werden.
Sie können prüfen, ob der Callback zu spät erfolgt ist (und der Frame bereits auf dem Bildschirm gerendert wurde), indem Sie prüfen, ob metadata.expectedDisplayTime
ungefähr now
oder ein V-Sync in der Zukunft ist.
Wenn der Wert innerhalb von etwa 5 bis 10 Mikrosekunden von now
liegt, wird der Frame bereits gerendert. Wenn expectedDisplayTime
etwa 16 Millisekunden in der Zukunft liegt (vorausgesetzt, Ihr Browser/Bildschirm wird mit 60 Hz aktualisiert), sind Sie mit dem Frame synchron.
Demo
Ich habe eine kleine Demo auf 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);
Zusammenfassung
Die Verarbeitung auf Frame-Ebene ist schon lange möglich – ohne Zugriff auf die tatsächlichen Frames, nur auf Grundlage von video.currentTime
.
Die requestVideoFrameCallback()
-Methode ist eine deutliche Verbesserung dieser Problemumgehung.
Danksagungen
Die requestVideoFrameCallback
API wurde von Thomas Guilbert spezifiziert und implementiert.
Dieser Beitrag wurde von Joe Medley und Kayce Basques geprüft.
Hero-Image von Denise Jans auf Unsplash.