Skip to main content

Learn to use the requestVideoFrameCallback () para trabajar de manera más eficiente con videos en el navegador.


Updated

There is a new web API in the block, defined in the
HTMLVideoElement.requestVideoFrameCallback ()

especificación. los requestVideoFrameCallback () El método permite a los autores web registrar una devolución de llamada que se ejecuta en los pasos de renderizado cuando se envía un nuevo fotograma de vídeo al compositor. Esto está destinado a permitir a los desarrolladores realizar operaciones eficientes por cuadro de video en video, como procesamiento de video y pintura en un lienzo, análisis de video o sincronización con fuentes de audio externas.

Difference with requestAnimationFrame ()

Operations such as drawing a video frame on a canvas using
drawImage ()

realizado a través de esta API se sincronizará como mejor esfuerzo con la velocidad de fotogramas del video que se reproduce en la pantalla. Diferente de
window.requestAnimationFrame (), which typically fires about 60 times per second,
requestVideoFrameCallback () está vinculado a la velocidad de fotogramas de vídeo real, con una importante
excepción:

La tasa efectiva a la que se ejecutan las devoluciones de llamada es la tasa menor entre la tasa del video y la tasa del navegador. Esto significa que un video de 25 fps que se reproduce en un navegador que pinta a 60Hz dispararía devoluciones de llamada a 25Hz. Un video de 120 fps en ese mismo navegador de 60Hz dispararía devoluciones de llamada a 60Hz.

¿Lo que hay en un nombre?

Due to its similarity to window.requestAnimationFrame (), el método inicialmente fue proposed as video.requestAnimationFrame (), but I'm happy with the new name,
requestVideoFrameCallback (), que se acordó tras un larga discusión. Hurrah, bikes ¡por la victoria!

Soporte de navegador y detección de funciones

El método es
implemented in Chromium
already, and
Mozilla people like. Por lo que vale, también he presentado una
WebKit error pedirlo. La detección de características de la API funciona así:

if ( 'requestVideoFrameCallback' in HTMLVideoElement . prototype ) {
}

Usando el método requestVideoFrameCallback ()

If you have ever used the requestAnimationFrame () método, inmediatamente se sentirá como en casa con el requestVideoFrameCallback () método. Usted registra una devolución de llamada inicial una vez y luego vuelve a registrarse cada vez que se activa la devolución de llamada.

const doSomethingWithTheFrame = ( now , metadata ) => {
console . log ( now , metadata ) ;
video . requestVideoFrameCallback ( doSomethingWithTheFrame ) ;
} ;
video . requestVideoFrameCallback ( doSomethingWithTheFrame ) ;

En la devolución de llamada, now is a DOMHighResTimeStamp

and metadata is a VideoFrameMetadata

dictionary with the following properties:

  • presentationTime, of type DOMHighResTimeStamp: El momento en el que el agente de usuario envió el marco para su composición.
  • expectedDisplayTime, of type DOMHighResTimeStamp: The time when the user agent expects the frame to be visible.
  • width, of type unsigned long: El ancho del fotograma de video, en píxeles de medios.
  • height, of type unsigned long: La altura del fotograma de vídeo, en píxeles multimedia.
  • mediaTime, of type double: La marca de tiempo de presentación de medios (PTS) en segundos del fotograma presentado (por ejemplo, su marca de tiempo en el video.currentTime línea de tiempo).
  • presentedFrames, of type unsigned long: Recuento del número de fotogramas enviados para composición. Permite a los clientes determinar si se perdieron tramas entre instancias de VideoFrameRequestCallback.
  • processingDuration, of type double: La duración transcurrida en segundos desde el envío del paquete codificado con la misma marca de tiempo de presentación (PTS) que esta trama (por ejemplo, igual que la mediaTime) al decodificador hasta que la trama decodificada esté lista para su presentación.

For WebRTC applications, additional properties may appear:

  • captureTime, of type DOMHighResTimeStamp: Para los fotogramas de video que provienen de una fuente local o remota, este es el momento en el que la cámara capturó el fotograma. Para una fuente remota, el tiempo de captura se estima mediante la sincronización del reloj y los informes del remitente RTCP para convertir las marcas de tiempo RTP en tiempo de captura.
  • receiveTime, of type DOMHighResTimeStamp: Para las tramas de video que provienen de una fuente remota, esta es la hora a la que la plataforma recibió la trama codificada, es decir, la hora a la que se recibió el último paquete perteneciente a esta trama a través de la red.
  • rtpTimestamp, of type unsigned long: La marca de tiempo RTP asociada con este fotograma de vídeo.

Note that width and height may differ from videoWidth and videoHeight en ciertos casos (por ejemplo, un video anamórfico puede tener píxeles rectangulares).

De especial interés en esta lista es mediaTime. En la implementación de Chromium, usamos el reloj de audio como fuente de tiempo que respalda video.currentTime, Meanwhile he mediaTime está poblado directamente por el presentationTimestamp of the frame. the mediaTime is what to use if you want to accurately identify the frames in a reproducible way, even to identify exactly the frames that were lost.

Desafortunadamente, el elemento de video no garantiza una precisión de cuadro searching. This has been a continuous tema de discusión.
WebCodecs eventualmente permitirá aplicaciones con precisión de cuadros.

Si las cosas parecen un cuadro de distancia …

La sincronización vertical (o simplemente vsync) es una tecnología de gráficos que sincroniza la frecuencia de cuadros de un video y la frecuencia de actualización de un monitor. Ya que requestVideoFrameCallback () se ejecuta en el hilo principal, pero, bajo el capó, la composición de video ocurre en el hilo del compositor, todo desde esta API es un mejor esfuerzo y no ofrecemos ninguna garantía estricta. Lo que puede estar sucediendo es que la API puede retrasarse un vsync en relación con el momento en que se procesa un fotograma de video. Se necesita un vsync para que los cambios realizados en la página web a través de la API aparezcan en la pantalla (igual que window.requestAnimationFrame ()). So if you keep updating mediaTime o el número de fotograma en su página web y compárelo con los fotogramas de video numerados, eventualmente el video se verá como si estuviera un fotograma por delante.

Lo que realmente está sucediendo es que el marco está listo en vsync x, la devolución de llamada se activa y el marco se procesa en vsync x + 1, y los cambios realizados en la devolución de llamada se procesan en vsync x + 2. Puede verificar si la devolución de llamada es un vsync tardío (y el marco ya está renderizado en la pantalla) verificando si el metadata.expectedDisplayTime is approximately now o un vsync en el futuro. Si está dentro de unos cinco a diez microsegundos de now, el marco ya está renderizado; Si el expectedDisplayTime es aproximadamente dieciséis milisegundos en el futuro (suponiendo que su navegador / pantalla se actualice a 60Hz), entonces está sincronizado con el marco.

Manifestación

He creado un pequeño
demostración en Glitch
que muestra cómo se dibujan los fotogramas en un lienzo exactamente a la velocidad de fotogramas del video y dónde se registran los metadatos del fotograma para fines de depuración. La lógica central es solo un par de líneas de JavaScript.

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);

Conclusions

He realizado procesamiento a nivel de fotogramas durante mucho tiempo, sin tener acceso a los fotogramas reales, solo en función de video.currentTime. Implementé la segmentación de tomas de video en JavaScript de una manera aproximada; todavía puedes leer el adjunto
trabajo de investigación. Tenía el requestVideoFrameCallback () existía en ese entonces, mi vida hubiera sido mucho más simple …

Thanks

the requestVideoFrameCallback The API was specified and implemented by
Thomas guilbert. Este artículo fue revisado por Joe medley
and Kayce Basques.
Imagen de héroe for
Denise Jans on Unsplash.