Tarayıcıda videolarla daha verimli çalışmak için requestVideoFrameCallback()
simgesini nasıl kullanacağınızı öğrenin.
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 işleme ve tuval üzerine çizim, video analizi veya harici ses kaynaklarıyla senkronizasyon gibi video üzerinde video karesi başına verimli işlemler gerçekleştirebilir.
requestAnimationFrame() ile arasındaki fark
Bu API aracılığıyla yapılan drawImage()
kullanılarak bir video karesini tuvale çizme gibi işlemler, ekranda oynatılan videonun kare hızıyla en iyi şekilde senkronize edilir.
Saniyede yaklaşık 60 kez tetiklenen window.requestAnimationFrame()
'den farklı olarak 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 hız, videonun hızı ile tarayıcının hızı arasındaki daha düşük olan hızdır. Bu, 60 Hz'de yeniden çizim yapan bir tarayıcıda oynatılan 25 FPS'lik bir videonun geri çağırmaları 25 Hz'de tetikleyeceği anlamına gelir. Aynı 60 Hz tarayıcıdaki 120 FPS video, geri çağırmaları 60 Hz'de tetikler.
Adın önemi var mı?
window.requestAnimationFrame()
ile benzerliği nedeniyle yöntem başlangıçta video.requestAnimationFrame()
olarak önerilmiş ve uzun bir tartışmanın ardından requestVideoFrameCallback()
olarak yeniden adlandırılmıştır.
Özellik algılama
if ('requestVideoFrameCallback' in HTMLVideoElement.prototype) {
// The API is supported!
}
Tarayıcı desteği
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.
requestVideoFrameCallback() yöntemini kullanma
Daha önce requestAnimationFrame()
yöntemini kullandıysanız requestVideoFrameCallback()
yöntemine hemen alışırsınız.
İlk geri aramayı bir kez kaydedersiniz ve geri arama her tetiklendiğinde yeniden kaydedersiniz.
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
,DOMHighResTimeStamp
türünde: Kullanıcı aracısının çerçeveyi oluşturma için gönderdiği zaman.expectedDisplayTime
,DOMHighResTimeStamp
tü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 çerçevesinin yüksekliği (medya pikseli cinsinden).mediaTime
,double
türünde: Gösterilen karenin saniye cinsinden medya sunumu zaman damgası (PTS) (ör.video.currentTime
zaman ç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ı (PTS) ile gönderilmesinden (ör.mediaTime
ile aynı) kod çözücüye kadar geçen süre (saniye cinsinden).
WebRTC uygulamaları için ek özellikler görünebilir:
captureTime
,DOMHighResTimeStamp
türünde: Yerel veya uzak bir kaynaktan gelen video kareleri için bu, karenin kamera tarafından çekildiği zamandır. Uzak bir kaynak için yakalama süresi, RTP zaman damgalarını yakalama süresine dönüştürmek üzere saat senkronizasyonu ve RTCP gönderen raporları kullanılarak tahmin edilir.receiveTime
,DOMHighResTimeStamp
türünde: 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 long
türünde: Bu video karesiyle ilişkili RTP zaman damgası.
Bu listede özellikle mediaTime
dikkat çekiyor.
Chromium'un uygulamasında, video.currentTime
öğesini destekleyen zaman kaynağı olarak ses saati kullanılırken mediaTime
, doğrudan karenin presentationTimestamp
öğesiyle doldurulur.
Çerçeveleri tam olarak tanımlamak istiyorsanız (hangi çerçeveleri kaçırdığınızı tam olarak belirlemek dahil) mediaTime
kullanmanız gerekir.
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ışsa da video birleştirme, oluşturucu 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 karesinin oluşturulmasına kıyasla bir dikey senkronizasyon 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ç mi olduğunu (ve karenin ekranda zaten oluşturulmuş olup olmadığını) metadata.expectedDisplayTime
değerinin yaklaşık olarak now
olup olmadığını veya bir vsync sonra olup olmadığını kontrol ederek doğrulayabilirsiniz.
now
'dan yaklaşık 5-10 mikrosaniye sonra ise kare zaten oluşturulmuştur. expectedDisplayTime
yaklaşık 16 milisaniye sonra ise (tarayıcınızın/ekranı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.
Unsplash'teki Denise Jans'ın hero resmi.