Tarayıcıda videolarla daha verimli çalışmak için requestVideoFrameCallback()
uygulamasını nasıl kullanacağınızı öğrenin.
HTMLVideoElement.requestVideoFrameCallback()
yöntemi, web yazarlarının, yeni bir video karesi kompozöre gönderildiğinde oluşturma adımlarında çalışan bir geri çağırma işlevi kaydetmesine olanak tanır.
Bu sayede geliştiriciler videoda video işleme ve tuvale boyama, video analizi veya harici ses kaynaklarıyla senkronizasyon gibi video karesi başına verimli işlemler gerçekleştirebilir.
requestAnimationFrame() ile fark
Bu API üzerinden yapılan drawImage()
kullanarak bir kanvas üzerine video karesi çizme gibi işlemler, ekranda oynatılan videonun kare hızıyla mümkün olduğunca senkronize edilir.
Genellikle saniyede yaklaşık 60 kez tetiklenen window.requestAnimationFrame()
'ten farklı olarak requestVideoFrameCallback()
, önemli bir istisna dışında 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ı hızının arasındaki daha düşük hızdır. Bu, 60 Hz'de boyama yapan bir tarayıcıda oynatılan 25 fps'lik bir videonun geri çağırma işlevlerini 25 Hz'de tetikleyeceği anlamına gelir. Aynı 60 Hz tarayıcıda 120 fps video, geri çağırma işlevlerini 60 Hz'de tetikler.
Adın önemi var mı?
window.requestAnimationFrame()
ile benzerliği nedeniyle, yöntem başlangıçta video.requestAnimationFrame()
olarak önerildi ve requestVideoFrameCallback()
olarak yeniden adlandırıldı. Yöntem, uzun bir tartışmadan sonra kararlaştırıldı.
Özellik algılama
if ('requestVideoFrameCallback' in HTMLVideoElement.prototype) {
// The API is supported!
}
Tarayıcı desteği
Çoklu dolgu
Window.requestAnimationFrame()
ve HTMLVideoElement.getVideoPlaybackQuality()
tabanlı bir requestVideoFrameCallback()
yöntemi için polyfill kullanılabilir. Bu özelliği kullanmadan önce README
bölümünde belirtilen sınırlamalara dikkat edin.
requestVideoFrameCallback() yöntemini kullanma
requestAnimationFrame()
yöntemini daha önce kullandıysanız requestVideoFrameCallback()
yöntemine hemen alışırsınız.
İlk geri aramayı bir kez kaydedersiniz ve geri arama 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ğırma işlevinde now
bir DOMHighResTimeStamp
, metadata
ise aşağıdaki özelliklere sahip bir VideoFrameMetadata
sözlüğüdür:
DOMHighResTimeStamp
türündekipresentationTime
: Kullanıcı aracısının, çerçeveyi oluşturma için gönderdiği zaman.DOMHighResTimeStamp
türündekiexpectedDisplayTime
: Kullanıcı aracısının çerçevenin görünür olmasını beklediği zaman.width
,unsigned long
türü: Video çerçevesinin genişliği (medya piksellerinde).height
,unsigned long
türü: Video çerçevesinin yüksekliği (medya pikseli cinsinden).mediaTime
,double
türü: Sunulan karenin saniye cinsinden medya sunma zaman damgası (PTS) (ör.video.currentTime
zaman çizelgesindeki zaman damgası).presentedFrames
,unsigned long
türü: Oluşturma için gönderilen kare sayısının sayısı. İstemcilerin,VideoFrameRequestCallback
örnekleri arasında kare atlanıp atlanmadığını belirlemesine olanak tanır.double
türündekiprocessingDuration
: Kodlanmış paketin, bu kareyle aynı sunma zaman damgasına (PTS) sahip olarak (ör.mediaTime
ile aynı) kod çözücüye gönderilmesinden, kod çözülmüş karenin sunulmaya hazır hale gelmesine kadar geçen süre (saniye cinsinden).
WebRTC uygulamaları için ek özellikler görünebilir:
captureTime
,DOMHighResTimeStamp
türü: Yerel veya uzak bir kaynaktan gelen video kareleri için bu, karenin kamera tarafından yakalandığı zamandır. Uzak bir kaynak için yakalama zamanı, RTP zaman damgalarını yakalama zamanına 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 kodlanmış karenin platform tarafından alındığı zamanı, yani bu kareye ait son paketin ağ üzerinden alındığı zamanı gösterir.rtpTimestamp
,unsigned long
türü: Bu video karesiyle ilişkili RTP zaman damgası.
Bu liste özellikle mediaTime
ilgilendiriyor.
Chromium uygulaması, video.currentTime
öğesini destekleyen zaman kaynağı olarak ses saatini kullanırken mediaTime
öğesi doğrudan karenin presentationTimestamp
öğesi tarafından doldurulur.
Kaçırdığınız kareleri tam olarak belirlemek de dahil olmak üzere, kareleri tekrarlanabilir bir şekilde tam olarak tanımlamak istiyorsanız mediaTime
kullanmanız gerekir.
Her şey bir kare uzak gibi görünüyorsa...
Dikey senkronizasyon (veya kısaca 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ışır ancak video oluşturma işlemi, arka planda oluşturucu iş parçacığında gerçekleşir. Bu nedenle, bu API'deki her şey olabildiğince yapılır ve tarayıcı herhangi bir kesin garanti sunmaz.
API, bir video çerçevesinin oluşturulmasına kıyasla bir vsync geç olabilir.
API aracılığıyla web sayfasında yapılan değişikliklerin ekranda görünmesi için bir vsync gerekir (window.requestAnimationFrame()
ile aynıdır). Dolayısıyla, web sayfanızdaki mediaTime
veya kare numarasını güncellemeye devam ederseniz ve bunu numaralı video kareleriyle karşılaştırırsanız video bir kare ilerideymiş gibi görünür.
Gerçekte olan şey, karenin vsync x'te hazır olması, geri çağırma işlevinin tetiklenmesi ve karenin vsync x+1'de oluşturulması, geri çağırma işlevinde yapılan değişikliklerin ise vsync x+2'de oluşturulmasıdır.
metadata.expectedDisplayTime
değerinin yaklaşık olarak now
değerine eşit olup olmadığını veya bir vsync sonra olup olmadığını kontrol ederek geri çağırma işlevinin vsync gecikmeli olup olmadığını (ve karenin ekranda zaten oluşturulup oluşturulmadığını) kontrol edebilirsiniz.
now
ile yaklaşık beş ila on mikrosaniye arasındaysa kare zaten oluşturulmuştur; expectedDisplayTime
yaklaşık on altı milisaniye sonraysa (tarayıcınızın/ekranınızın 60 Hz'de yenilendiği varsayılırsa) kareyle senkronize olursunuz.
Demo
Tam olarak videonun kare hızında bir kanvas üzerinde karelerin nasıl çizildiğini ve hata ayıklama amacıyla kare meta verilerinin nereye kaydedildiğini gösteren küçük bir Glitch demosu 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 kare düzeyinde işlem yapıyor. Bunun için gerçek karelere erişmek yerine yalnızca video.currentTime
'e dayanıyor.
requestVideoFrameCallback()
yöntemi, bu geçici çözümü büyük ölçüde iyileştirir.
Teşekkür ederiz
requestVideoFrameCallback
API'si Thomas Guilbert tarafından tanımlanıp uygulanmıştır.
Bu yayın Joe Medley ve Kayce Basques tarafından incelendi.
Unsplash'taki Denise Jans tarafından oluşturulan lokomotif resim.