Pelajari cara menggunakan requestVideoFrameCallback()
untuk bekerja lebih efisien dengan video di browser.
Metode HTMLVideoElement.requestVideoFrameCallback()
memungkinkan penulis web mendaftarkan callback yang berjalan dalam langkah-langkah rendering saat frame video baru dikirim ke compositor.
Hal ini memungkinkan developer menjalankan operasi per frame video yang efisien pada video,
seperti pemrosesan dan pengecatan video ke kanvas, analisis video,
atau sinkronisasi dengan sumber audio eksternal.
Perbedaan dengan requestAnimationFrame()
Operasi seperti menggambar frame video ke kanvas menggunakan
drawImage()
yang dibuat melalui API ini akan disinkronkan sebagai upaya terbaik
dengan kecepatan frame video yang diputar di layar.
Berbeda dengan
window.requestAnimationFrame()
,
yang biasanya diaktifkan sekitar 60 kali per detik,
requestVideoFrameCallback()
terikat dengan kecepatan frame video yang sebenarnya—dengan
pengecualian penting:
Tingkat efektif saat callback dijalankan adalah kecepatan yang lebih rendah antara kecepatan video dan kecepatan browser. Ini berarti video 25 fps yang diputar di browser yang digambar pada 60 Hz akan memicu callback pada 25 Hz. Video 120 fps dalam browser 60 Hz yang sama akan memicu callback pada 60 Hz.
Apalah arti sebuah nama?
Karena kemiripannya dengan window.requestAnimationFrame()
, metode ini awalnya diusulkan sebagai video.requestAnimationFrame()
dan diganti namanya menjadi requestVideoFrameCallback()
, yang disetujui setelah diskusi yang panjang.
Deteksi fitur
if ('requestVideoFrameCallback' in HTMLVideoElement.prototype) {
// The API is supported!
}
Dukungan browser
Isi Ulang
Tersedia polyfill untuk metode requestVideoFrameCallback()
berdasarkan Window.requestAnimationFrame()
dan HTMLVideoElement.getVideoPlaybackQuality()
. Sebelum menggunakan ini, perhatikan
batasan yang disebutkan dalam README
.
Menggunakan metode requestVideoFrameCallback()
Jika pernah menggunakan metode requestAnimationFrame()
, Anda akan langsung terbiasa dengan metode requestVideoFrameCallback()
.
Anda mendaftarkan callback awal satu kali, lalu mendaftarkan ulang setiap kali callback diaktifkan.
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);
Dalam callback, now
adalah DOMHighResTimeStamp
dan metadata
adalah kamus VideoFrameMetadata
dengan properti berikut:
presentationTime
, dari jenisDOMHighResTimeStamp
: Waktu saat agen pengguna mengirimkan frame untuk komposisi.expectedDisplayTime
, dari jenisDOMHighResTimeStamp
: Waktu saat agen pengguna mengharapkan frame terlihat.width
, dari jenisunsigned long
: Lebar frame video, dalam piksel media.height
, dari jenisunsigned long
: Tinggi frame video, dalam piksel media.mediaTime
, dari jenisdouble
: Stempel waktu presentasi media (PTS) dalam detik dari frame yang ditampilkan (misalnya, stempel waktu di linimasavideo.currentTime
).presentedFrames
, dari jenisunsigned long
: Jumlah jumlah frame yang dikirimkan untuk komposisi. Memungkinkan klien menentukan apakah frame terlewat di antara instanceVideoFrameRequestCallback
.processingDuration
, dari jenisdouble
: Durasi yang berlalu dalam detik sejak pengiriman paket yang dienkode dengan stempel waktu presentasi (PTS) yang sama dengan frame ini (misalnya, sama denganmediaTime
) ke decoder hingga frame yang didekode siap untuk presentasi.
Untuk aplikasi WebRTC, properti tambahan mungkin muncul:
captureTime
, dari jenisDOMHighResTimeStamp
: Untuk frame video yang berasal dari sumber lokal atau jarak jauh, ini adalah waktu saat frame diambil oleh kamera. Untuk sumber jarak jauh, waktu pengambilan diperkirakan menggunakan sinkronisasi jam dan laporan pengirim RTCP untuk mengonversi stempel waktu RTP guna merekam waktu.receiveTime
, dari jenisDOMHighResTimeStamp
: Untuk frame video yang berasal dari sumber jarak jauh, ini adalah waktu saat frame yang dienkode diterima oleh platform, yaitu waktu saat paket terakhir milik frame ini diterima melalui jaringan.rtpTimestamp
, dari jenisunsigned long
: Stempel waktu RTP yang terkait dengan frame video ini.
Yang memiliki minat khusus dalam daftar ini adalah mediaTime
.
Implementasi Chromium menggunakan jam audio sebagai sumber waktu yang mendukung video.currentTime
,
sedangkan mediaTime
diisi langsung oleh presentationTimestamp
frame.
mediaTime
adalah yang harus Anda gunakan jika Anda ingin mengidentifikasi secara tepat frame dengan cara yang dapat direproduksi,
termasuk untuk mengidentifikasi secara persis frame mana yang Anda lewatkan.
Jika semuanya tampak satu frame...
Sinkronisasi vertikal (atau hanya vsync), adalah teknologi grafis yang menyinkronkan kecepatan frame video dan kecepatan refresh monitor.
Karena requestVideoFrameCallback()
berjalan di thread utama, tetapi pada dasarnya, pengomposisian video terjadi di thread compositor,
segala sesuatu dari API ini merupakan upaya terbaik, dan browser tidak menawarkan jaminan ketat.
Yang mungkin terjadi adalah API tersebut dapat menjadi salah satu vsync yang terlambat dibandingkan dengan saat frame video dirender.
Dibutuhkan satu vsync untuk perubahan yang dibuat pada halaman web melalui API agar muncul di layar (sama seperti window.requestAnimationFrame()
).
Jadi, jika Anda terus memperbarui mediaTime
atau nomor frame di halaman web dan membandingkannya dengan frame video bernomor, pada akhirnya video akan terlihat seperti satu frame di depan.
Yang sebenarnya terjadi adalah frame siap pada vsync x, callback diaktifkan dan frame dirender pada vsync x+1,
dan perubahan yang dibuat dalam callback dirender pada vsync x+2.
Anda dapat memeriksa apakah callback merupakan vsync yang terlambat (dan frame sudah dirender di layar)
dengan memeriksa apakah metadata.expectedDisplayTime
kira-kira now
atau satu vsync di masa mendatang.
Jika dalam waktu sekitar lima hingga sepuluh mikrodetik now
, frame sudah dirender;
jika expectedDisplayTime
sekitar enam belas milidetik di masa mendatang (dengan asumsi browser/layar dimuat ulang pada 60 Hz),
maka Anda telah sinkron dengan frame.
Demo
Saya telah membuat demo kecil di Glitch yang menunjukkan cara frame digambar di kanvas dengan kecepatan frame video yang sama persis dan di mana metadata frame dicatat ke dalam log untuk tujuan proses debug.
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);
Kesimpulan
Pengguna telah melakukan pemrosesan tingkat frame untuk waktu yang lama—tanpa memiliki akses ke frame yang sebenarnya,
hanya berdasarkan video.currentTime
.
Metode requestVideoFrameCallback()
sangat meningkatkan solusi ini.
Ucapan terima kasih
requestVideoFrameCallback
API ditentukan dan diimplementasikan oleh
Thomas Guilbert.
Postingan ini ditinjau oleh Joe Medley
dan Kayce Basques.
Banner besar oleh
Denise Jans di Unsplash.