Hier erfahren Sie, wie Sie mithilfe von requestVideoFrameCallback()
effizienter mit Videos im Browser arbeiten.
Mit der Methode HTMLVideoElement.requestVideoFrameCallback()
können Webautoren einen Rückruf registrieren, der in den Rendering-Schritten ausgeführt wird, wenn ein neuer Videoframe an den Renderer gesendet wird.
So können Entwickler effiziente Vorgänge pro Videoframe ausführen, z. B. Videoverarbeitung und Zeichnen auf einem Canvas, Videoanalyse oder Synchronisierung mit externen Audioquellen.
Unterschied zu requestAnimationFrame()
Vorgänge wie das Zeichnen eines Videoframes mit drawImage()
über diese API auf einem Canvas werden nach Möglichkeit mit der Framerate des auf dem Bildschirm wiedergegebenen Videos synchronisiert.
Im Gegensatz 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 niedrigere Rate zwischen der Rate des Videos und der Rate des Browsers. Das bedeutet, dass ein Video mit 25 fps, das in einem Browser abgespielt wird, der mit 60 Hz wiedergegeben wird, Rückrufe bei 25 Hz auslösen. Bei einem Video mit 120 fps in demselben 60-Hz-Browser werden 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 längeren Diskussion vereinbart wurde.
Funktionserkennung
if ('requestVideoFrameCallback' in HTMLVideoElement.prototype) {
// The API is supported!
}
Unterstützte Browser
Polyfill
Es ist ein Polyfill für die requestVideoFrameCallback()
-Methode verfügbar, das auf Window.requestAnimationFrame()
und HTMLVideoElement.getVideoPlaybackQuality()
basiert. Bevor Sie diese Funktion verwenden, sollten Sie die Einschränkungen in README
beachten.
Mit der Methode „requestVideoFrameCallback()“
Wenn Sie schon einmal die Methode requestAnimationFrame()
verwendet haben, werden Sie mit der Methode requestVideoFrameCallback()
sofort vertraut sein.
Sie registrieren einen ersten Callback einmal und registrieren ihn dann noch einmal, 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 Eigenschaften:
presentationTime
vom TypDOMHighResTimeStamp
: Der Zeitpunkt, zu dem der User-Agent den Frame zur Zusammensetzung 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 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 angezeigten Frames (z.B. sein Zeitstempel in der Zeitleistevideo.currentTime
).presentedFrames
vom Typunsigned long
: Anzahl der Frames, die für die Komposition eingereicht wurden. Ermöglicht es Clients, festzustellen, ob Frames zwischen den Instanzen vonVideoFrameRequestCallback
übersprungen wurden.processingDuration
vom Typdouble
: Die verstrichene Zeit in Sekunden, die vergangen ist, seit das codierte Paket mit demselben Präsentationszeitstempel (PTS) wie dieser Frame (z.B. demmediaTime
) an den Decoder gesendet wurde, bis der decodierte Frame zur Präsentation bereit war.
Für WebRTC-Anwendungen können zusätzliche Eigenschaften angezeigt werden:
captureTime
vom TypDOMHighResTimeStamp
: Bei Videoframes, die von einer lokalen oder Remote-Quelle stammen, ist dies die Zeit, zu der der Frame von der Kamera aufgenommen wurde. Bei einer Remotequelle wird die Erfassungszeit anhand der Uhrsynchronisierung und RTCP-Senderberichte geschätzt, um RTP-Zeitstempel in die Erfassungszeit umzuwandeln.receiveTime
vom TypDOMHighResTimeStamp
: Bei Videoframes, die von einer Remotequelle stammen, ist dies die Zeit, zu der der codierte Frame von der Plattform empfangen wurde. Das ist die Zeit, zu der 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 in dieser Liste ist mediaTime
.
Bei der Chromium-Implementierung wird die Audiouhr als Zeitquelle verwendet, die video.currentTime
unterstützt, während mediaTime
direkt durch das presentationTimestamp
des Frames ausgefüllt wird.
Die mediaTime
sollten Sie verwenden, wenn Sie Frames auf reproduzierbare Weise genau identifizieren möchten, einschließlich der Frames, die Sie verpasst haben.
Wenn etwas einen Frame zu spät erscheint…
Vertikale Synchronisierung (oder einfach vsync) ist eine Grafiktechnologie, die die Framerate eines Videos und die Aktualisierungsrate eines Monitors synchronisiert.
Da requestVideoFrameCallback()
im Haupt-Thread ausgeführt wird, das Video-Compositing aber im Compositor-Thread erfolgt, ist alles, was von dieser API zurückgegeben wird, ein Best-Effort-Verfahren und der Browser bietet keine strengen Garantien.
Möglicherweise ist die API im Vergleich zum Rendern eines Videoframes um eine vsync zu spät.
Es dauert einen VSync, bis Änderungen an der Webseite über die API auf dem Bildschirm angezeigt werden (wie bei window.requestAnimationFrame()
). Wenn du also die mediaTime
oder die Frame-Nummer auf deiner Webseite immer wieder aktualisierst und mit den nummerierten Videoframes vergleichst, sieht das Video irgendwann so aus, als wäre es einen Frame voraus.
Was tatsächlich passiert, ist, dass der Frame bei vsync x bereit ist, der Callback ausgelöst und bei vsync x+1 gerendert wird. Änderungen im Callback werden bei vsync x+2 gerendert.
Sie können prüfen, ob der Rückruf um eine VSync-Periode zu spät erfolgt (und der Frame bereits auf dem Bildschirm gerendert wurde), indem Sie prüfen, ob metadata.expectedDisplayTime
ungefähr now
oder eine VSync-Periode in der Zukunft ist.
Wenn der Wert innerhalb von etwa fünf bis zehn Mikrosekunden von now
liegt, wurde 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 Debug-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);
Schlussfolgerungen
Nutzer haben schon lange die Verarbeitung auf Frameebene durchgeführt, ohne Zugriff auf die tatsächlichen Frames zu haben, nur basierend auf video.currentTime
.
Die requestVideoFrameCallback()
-Methode verbessert diese Problemumgehung erheblich.
Danksagungen
Die requestVideoFrameCallback
API wurde von Thomas Guilbert spezifiziert und implementiert.
Dieser Beitrag wurde von Joe Medley und Kayce Basques überprüft.
Hero-Image von Denise Jans auf Unsplash.