Dowiedz się, jak korzystać z requestVideoFrameCallback()
, aby efektywniej pracować z filmami w przeglądarce.
Metoda HTMLVideoElement.requestVideoFrameCallback()
umożliwia autorom stron internetowych rejestrowanie wywołania zwrotnego, które jest uruchamiane w krokach renderowania, gdy do kompozytora jest wysyłana nowa klatka wideo.
Dzięki temu deweloperzy mogą wykonywać efektywne operacje na klatce filmu, np. przetwarzanie i malowanie obrazu na płótnie, analizę wideo lub synchronizację z zewnętrznymi źródłami audio.
Różnica w metodzie requestAnimationFrame()
Operacje takie jak rysowanie klatki wideo do obszaru roboczego za pomocą drawImage()
wykonane za pomocą tego interfejsu API będą w miarę możliwości synchronizowane z liczbą klatek filmu odtwarzanego na ekranie.
W przeciwieństwie do metody window.requestAnimationFrame()
, która zwykle uruchamia się około 60 razy na sekundę, parametr requestVideoFrameCallback()
jest powiązany z rzeczywistą liczbą klatek na sekundę – z ważnym wyjątkiem:
Efektywna częstotliwość wykonywania wywołań zwrotnych to mniejsza wartość między częstotliwością reklamy wideo a częstotliwością przeglądarki. Oznacza to, że film z szybkością 25 kl./s odtwarzany w przeglądarce, który maluje z częstotliwością 60 Hz, uruchamia wywołania zwrotne z częstotliwością 25 Hz. Film z szybkością 120 kl./s w tej samej przeglądarce 60 Hz uruchamiałby wywołania zwrotne z częstotliwością 60 Hz.
Co kryje się pod nazwą?
Ze względu na podobieństwo do metody window.requestAnimationFrame()
początkowo została ona zaproponowana jako video.requestAnimationFrame()
i zmieniła nazwę na requestVideoFrameCallback()
, co zostało uzgodnione po długiej dyskusji.
Wykrywanie funkcji
if ('requestVideoFrameCallback' in HTMLVideoElement.prototype) {
// The API is supported!
}
Obsługiwane przeglądarki
Włókno poliestrowe
Dostępny jest kod polyfill dla metody requestVideoFrameCallback()
oparty na Window.requestAnimationFrame()
i HTMLVideoElement.getVideoPlaybackQuality()
. Zanim z niego skorzystasz, zapoznaj się z ograniczeniami wymienionymi w README
.
Korzystanie z metody requestVideoFrameCallback()
Jeśli kiedykolwiek korzystałeś(-aś) z metody requestAnimationFrame()
, od razu znasz metodę requestVideoFrameCallback()
.
Jednorazowo rejestrujesz pierwsze wywołanie zwrotne, a potem rejestrujesz się ponownie po uruchomieniu wywołania zwrotnego.
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);
W wywołaniu zwrotnym now
to język DOMHighResTimeStamp
, a metadata
to słownik VideoFrameMetadata
z tymi właściwościami:
presentationTime
, typDOMHighResTimeStamp
: czas, w którym klient użytkownika przesłał ramkę do kompozycji.expectedDisplayTime
, typDOMHighResTimeStamp
: godzina, o której klient użytkownika spodziewa się zobaczyć klatkę.width
, typunsigned long
: szerokość klatki wideo w pikselach multimediów.height
, typunsigned long
: wysokość klatki wideo (w pikselach multimediów).mediaTime
typudouble
: sygnatura czasowa prezentacji multimediów (PTS) w sekundach wyświetlonej klatki (np. jej sygnatura czasowa na osi czasuvideo.currentTime
).presentedFrames
, typunsigned long
: liczba klatek przesłanych do kompozycji. Umożliwia klientom określenie, czy klatki zostały pominięte między instancjamiVideoFrameRequestCallback
.processingDuration
, typudouble
: Czas, jaki upływa od przesłania zakodowanego pakietu o tej samej sygnaturze czasowej prezentacji (PTS) co ta ramka (np.mediaTime
) do dekodera do momentu, gdy zdekodowana ramka była gotowa do prezentacji.
W przypadku aplikacji WebRTC mogą pojawić się dodatkowe właściwości:
captureTime
, typuDOMHighResTimeStamp
: w przypadku klatek wideo pochodzących ze źródła lokalnego lub zdalnego jest to godzina zarejestrowania klatki przez kamerę. W przypadku źródła zdalnego czas przechwytywania jest szacowany na podstawie synchronizacji zegara i raportów nadawców RTCP w celu konwersji sygnatur czasowych RTP na czas rejestracji.receiveTime
typuDOMHighResTimeStamp
: w przypadku klatek wideo pochodzących ze źródła zdalnego jest to czas odebrania zakodowanej ramki przez platformę, czyli czas odebrania przez sieć ostatniego pakietu należącego do tej klatki.rtpTimestamp
, typuunsigned long
: sygnatura czasowa RTP powiązana z tą klatką wideo.
Ta lista ma szczególne znaczenie: mediaTime
.
Implementacja Chromium używa zegara audio jako źródła czasu wspierającego parametr video.currentTime
, podczas gdy pole mediaTime
jest bezpośrednio wypełniane przez presentationTimestamp
ramki.
Atrybutu mediaTime
należy użyć, jeśli chcesz dokładnie identyfikować klatki, które można odtworzyć, w tym ustalać, które klatki zostały pominięte.
Jeśli obraz wydaje Ci się odsunięty w jedną klatkę...
Synchronizacja pionowa (lub zwykła vsync) to technologia graficzna, która synchronizuje liczbę klatek filmu i częstotliwość odświeżania na monitorze.
Interfejs requestVideoFrameCallback()
działa w wątku głównym, ale komponowanie wideo odbywa się w wątku kompozytora, dlatego wszystko robimy przy użyciu tego interfejsu API, a przeglądarka nie daje żadnych ścisłych gwarancji.
Wynika to z tego, że interfejs API może być opóźniony o jedną późną synchronizację w stosunku do czasu renderowania klatki wideo.
Zmiany wprowadzone przez interfejs API są widoczne na ekranie dopiero po jednej sesji vsync (tak samo jak w przypadku window.requestAnimationFrame()
).
Jeśli więc stale aktualizujesz numer mediaTime
lub numer klatki na stronie internetowej i porównujesz go z numerowanymi klatkami wideo, w końcu film będzie wyglądać tak, jakby był o jedną klatkę do przodu.
W rzeczywistości ramka jest gotowa na vsync x, wywoływane jest wywołanie zwrotne, a klatka renderowana jest przy użyciu vsync x+1, a zmiany w wywołaniu zwrotnym są renderowane przy użyciu vsync x+2.
Aby sprawdzić, czy wywołanie zwrotne jest opóźnione w przypadku vsync (a klatka została już wyrenderowana na ekranie), sprawdź, czy metadata.expectedDisplayTime
to około now
czy 1 synchronizacja vsync w przyszłości.
Jeśli od now
do 5–10 mikrosekund, klatka jest już renderowana. Jeśli expectedDisplayTime
będzie renderować się za około 16 milisekund (przy założeniu, że przeglądarka/ekran jest odświeżana przy 60 Hz), następuje synchronizacja z klatką.
Pokaz
Przygotowałem(-am) krótką prezentację w Glitchu, która pokazuje, jak klatki są rysowane na płótnie z taką samą liczbą klatek na sekundę, jaka jest liczba klatek filmu oraz gdzie są rejestrowane metadane klatek na potrzeby debugowania.
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);
Podsumowanie
Użytkownicy już od dawna korzystają z przetwarzania na poziomie klatki, nie mając dostępu do samych klatek, tylko na podstawie danych video.currentTime
.
Metoda requestVideoFrameCallback()
znacznie usprawnia to obejście.
Podziękowania
Interfejs API requestVideoFrameCallback
został stworzony i wdrożony przez Thomasa Guilberta.
Ten post został zweryfikowany przez Joe Medley
i Kayce Basques.
Baner powitalny autorstwa Denise Jans w aplikacji Unsplash.