Tarayıcıda videolarla daha verimli çalışmak için requestVideoFrameCallback() simgesini nasıl kullanacağınızı öğrenin.
Yayınlanma tarihi: 8 Ocak 2023
HTMLVideoElement.requestVideoFrameCallback()
Yöntemi, web yazarlarının yeni bir video karesi oluşturucuya gönderildiğinde oluşturma adımlarında çalışan bir geri çağırma kaydetmesine olanak tanır.
Bu sayede geliştiriciler, video üzerinde video işleme ve tuval üzerine çizim, video analizi veya harici ses kaynaklarıyla senkronizasyon gibi video kare başına verimli işlemler gerçekleştirebilir.
requestAnimationFrame() ile arasındaki fark
Bu API ile yapılan işlemler (ör. drawImage() ile bir video karesini tuvale çizme), ekranda oynatılan videonun kare hızıyla en iyi şekilde senkronize edilir. Bu, genellikle saniyede yaklaşık 60 kez tetiklenen window.requestAnimationFrame()'den farklıdır.
requestVideoFrameCallback(), önemli bir istisna ile birlikte gerçek video kare hızına bağlıdır:
Geri çağırmaların çalıştırıldığı etkili oran, videonun oranı ile tarayıcının oranı arasındaki daha düşük orandır. Bu, 60 Hz'de yeniden çizim yapan bir tarayıcıda oynatılan 25 FPS'lik bir videonun 25 Hz'de geri çağırma işlevlerini tetikleyeceği anlamına gelir. Aynı 60 Hz tarayıcıdaki 120 FPS video, geri çağırmaları 60 Hz'de tetikler.
Özellik algılama
if ('requestVideoFrameCallback' in HTMLVideoElement.prototype) {
// The API is supported!
}
Polyfill
requestVideoFrameCallback() yöntemi için
Window.requestAnimationFrame()
ve
HTMLVideoElement.getVideoPlaybackQuality()
tabanlı bir polyfill mevcuttur. Bu özelliği kullanmadan önce README bölümünde belirtilen sınırlamalara dikkat edin.
Yöntemi kullanma
requestAnimationFrame() yöntemini kullanırsanız requestVideoFrameCallback() yöntemini tanırsınız. İlk geri aramayı bir kez kaydedin ve geri arama her tetiklendiğinde yeniden kaydedin.
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);
Geri çağırmada now, DOMHighResTimeStamp, metadata ise aşağıdaki özelliklere sahip bir VideoFrameMetadata sözlüktür:
presentationTime,DOMHighResTimeStamptüründe: Kullanıcı aracısının çerçeveyi oluşturma için gönderdiği zaman.expectedDisplayTime,DOMHighResTimeStamptüründe: Kullanıcı aracısının, çerçevenin görünür olmasını beklediği zaman.width, türündeunsigned long: Video çerçevesinin genişliği (medya pikseli cinsinden).height, türündeunsigned long: Video karesinin yüksekliği (medya pikseli cinsinden).mediaTime, türündedouble: Sunulan karenin saniye cinsinden medya sunumu zaman damgası (PTS) (ör.video.currentTimezaman çizelgesindeki zaman damgası).presentedFrames, türündeunsigned long: Beste için gönderilen karelerin sayısı. İstemcilerin,VideoFrameRequestCallbackörnekleri arasında karelerin atlanıp atlanmadığını belirlemesine olanak tanır.processingDuration, türündedouble: Kodlanmış paketin bu kareyle aynı sunum zaman damgasıyla (PTS) (ör.mediaTimeile aynı) gönderilmesinden, kod çözülmüş karenin sunuma hazır hale gelmesine kadar geçen süre (saniye cinsinden).
WebRTC uygulamaları için ek özellikler görünebilir:
captureTime, türüDOMHighResTimeStamp: Yerel veya uzak bir kaynaktan gelen video kareleri için bu, karenin kamera tarafından yakalandığı zamandır. Uzak bir kaynak için yakalama süresi, saat senkronizasyonu ve RTCP gönderen raporları kullanılarak tahmin edilir. Bu raporlar, RTP zaman damgalarını yakalama süresine dönüştürmek için kullanılır.receiveTime, türüDOMHighResTimeStamp: Uzak bir kaynaktan gelen video kareleri için bu, kodlanmış karenin platform tarafından alındığı zamandır. Yani bu kareye ait son paketin ağ üzerinden alındığı zamandır.rtpTimestamp,unsigned longtüründe: Bu video karesiyle ilişkili RTP zaman damgası.
Bu listede özellikle mediaTime dikkat çekiyor.
Chromium'un uygulamasında, video.currentTime için zaman kaynağı olarak ses saati kullanılırken mediaTime, karenin presentationTimestamp değeriyle doğrudan doldurulur.
Çerçeveleri tam olarak tanımlamak istiyorsanız mediaTime kullanmanız gerekir. Bu, kaçırdığınız çerçeveleri tam olarak tanımlamak için de geçerlidir.
Her şey bir kare kaymış gibi görünüyorsa
Dikey senkronizasyon (veya yalnızca vsync), bir videonun kare hızını ve monitörün yenileme hızını senkronize eden bir grafik teknolojisidir.
requestVideoFrameCallback() ana iş parçacığında çalıştığından ancak arka planda video birleştirme işlemi birleştirici iş parçacığında gerçekleştiğinden bu API'den gelen her şey en iyi çaba ile yapılır ve tarayıcı herhangi bir katı garanti vermez.
API, bir video karesi oluşturulduğunda vsync'den bir vsync geç kalmış olabilir. API aracılığıyla web sayfasında yapılan değişikliklerin ekranda görünmesi için bir dikey senkronizasyon gerekir (window.requestAnimationFrame() ile aynı). Bu nedenle, web sayfanızdaki mediaTime veya kare numarasını sürekli olarak güncelleyip bunu numaralandırılmış video kareleriyle karşılaştırırsanız video sonunda bir kare ileride görünür.
Aslında olan şudur: Kare, dikey senkronizasyon x'te hazır olur, geri çağırma tetiklenir ve kare, dikey senkronizasyon x+1'de oluşturulur. Geri çağırmada yapılan değişiklikler ise dikey senkronizasyon x+2'de oluşturulur.
Geri çağırmanın vsync geç olup olmadığını (ve karenin ekranda zaten oluşturulup oluşturulmadığını) metadata.expectedDisplayTime değerinin yaklaşık olarak now olup olmadığını veya bir vsync sonra olup olmadığını kontrol ederek anlayabilirsiniz.
now ile yaklaşık beş ila on mikrosaniye arasındaysa kare zaten oluşturulmuştur. expectedDisplayTime yaklaşık on altı milisaniye sonrasındaysa (tarayıcınızın 60 Hz'de yenilendiği varsayılarak) kareyle senkronize olursunuz.
Demo
Karelerin tam olarak videonun kare hızında bir tuval üzerine nasıl çizildiğini ve hata ayıklama amacıyla kare meta verilerinin nereye kaydedildiğini gösteren küçük bir demo oluşturdum.
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);
Sonuçlar
Kullanıcılar, uzun süredir gerçek karelere erişmeden yalnızca video.currentTime temelinde kare düzeyinde işleme yapıyor.
requestVideoFrameCallback() yöntemi, bu geçici çözümü büyük ölçüde iyileştirir.
Teşekkür
requestVideoFrameCallback API'si Thomas Guilbert tarafından belirtilmiş ve uygulanmıştır.
Bu gönderi, Joe Medley ve Kayce Basques tarafından incelendi.