Nazywam się Dale Curtis i prowadzę zespół inżynierów odpowiedzialny za odtwarzanie multimediów w Chromium. Mój zespół odpowiada za interfejsy API dla przeglądarek służące do odtwarzania filmów, takie jak MSE i WebCodecs, oraz wewnętrzne mechanizmy platformy związane z demuxingiem, dekodowaniem i renderowaniem dźwięku i obrazu.
W tym artykule omawiam architekturę renderowania wideo w Chromium. Chociaż niektóre szczegóły dotyczące rozszerzalności są prawdopodobnie specyficzne dla Chromium, większość omawianych tu koncepcji i projektów dotyczy innych silników renderowania, a nawet natywnych aplikacji do odtwarzania.
Architektura odtwarzania w Chromium zmieniła się znacząco na przestrzeni lat. Chociaż nie zaczęliśmy od piramidy sukcesu, jak opisano w pierwszym poście z tej serii, ostatecznie postępowaliśmy podobnie: najpierw niezawodność, potem wydajność, a na końcu możliwość rozbudowy.
Na początku renderowanie filmów było dość proste – tylko pętla for wybierała, które dekodowane przez oprogramowanie klatki wideo mają zostać wysłane do kompozytora. Przez wiele lat było to wystarczająco niezawodne, ale wraz ze wzrostem złożoności internetu potrzeba zwiększenia wydajności i skuteczności doprowadziła do zmian w architekturze. Wiele ulepszeń wymagało prymitywów specyficznych dla systemu operacyjnego. Dlatego nasza architektura musiała stać się bardziej rozszerzalna, aby docierać do wszystkich platform Chromium.
Przetwarzanie filmu można podzielić na 2 etapy: wybór tego, co ma zostać przesłane, i skuteczne przesłanie tych informacji. W trosce o czytelność tekstu najpierw omówię wydajne dostarczanie, a potem wyjaśnię, jak Chromium wybiera treści do dostarczenia.
Niektóre warunki i układ
Ponieważ ten artykuł skupia się na renderowaniu, tylko pobieżnie omówię demultipleksowanie i dekodowanie.
Dekodowanie i demuxowanie w dzisiejszym świecie, w którym bezpieczeństwo jest tak ważne, wymaga sporo uwagi. Przetwarzacze binarne to bogate środowiska docelowe, a odtwarzanie multimediów to proces, w którym często występuje przetwarzanie binarne. W związku z tym problemy z bezpieczeństwem w parsowaniu multimediów są bardzo częste.
Chromium stosuje ochronę wielowarstwową, aby zmniejszyć ryzyko wystąpienia problemów z bezpieczeństwem u użytkowników. W praktyce oznacza to, że demuxowanie i dekodowanie za pomocą oprogramowania zawsze odbywają się w procesie o ograniczonych uprawnieniach, a dekodowanie sprzętowe odbywa się w procesie z wystarczającymi uprawnieniami do komunikacji z procesorem graficznym systemu.
Mechanizm komunikacji między procesami w Chromium nosi nazwę Mojo. W tym artykule nie będziemy szczegółowo omawiać Mojo, ale jako warstwa abstrakcji między procesami jest ona podstawą rozszerzalnego kanału multimedialnego w Chromium. Warto o tym pamiętać, gdy omawiamy ścieżkę odtwarzania, ponieważ wpływa ona na złożoną aranżację komponentów między procesami, które współpracują ze sobą, aby odbierać, demuxować, dekodować i ostatecznie wyświetlać media.
Tyle informacji
Aby zrozumieć dzisiejsze ścieżki renderowania wideo, trzeba wiedzieć, co sprawia, że wideo jest wyjątkowe: przepustowość. Odtwarzanie w rozdzielczości 3840 x 2160 (4K) z prędkością 60 kl./s zużywa od 9 do 12 gigabitów na sekundę przepustowości pamięci. Chociaż nowoczesne systemy mogą mieć przepustowość szczytową sięgającą setek gigabitów na sekundę, odtwarzanie filmów nadal pochłania znaczną część tej przepustowości. Bez odpowiedniej ostrożności łączna przepustowość może się łatwo zwiększyć ze względu na kopie lub przejazdy między pamięcią GPU a procesora.
Celem każdego nowoczesnego mechanizmu odtwarzania filmów, który ma na uwadze wydajność, jest zminimalizowanie przepustowości między dekoderem a końcowym krokiem renderowania. Z tego powodu renderowanie filmów jest w dużej mierze odłączone od głównego potoku renderowania w Chromium. W szczególności z perspektywy głównego kanału renderowania film to tylko otwór o stałym rozmiarze i przezroczystości. Chromium osiąga to za pomocą koncepcji powierzchni, w której każdy film jest bezpośrednio połączony z Viz.
Ze względu na popularność urządzeń mobilnych, wydajność i wydajność stały się ważnym elementem obecnej generacji. W efekcie dekodowanie i renderowanie są bardziej powiązane niż kiedykolwiek na poziomie sprzętu, co powoduje, że film wygląda jak dziura z przezroczystością, nawet dla samego systemu operacyjnego. Dekodery na poziomie platformy często udostępniają tylko nieprzezroczyste bufory, które Chromium przekazuje do systemu kompozytowania na poziomie platformy w postaci nakładek.
Każda platforma ma własną formę nakładek, z którymi współpracują interfejsy API do dekodowania. Windows ma Direct Composition i Media Foundation Transforms, macOS ma CoreAnimation Layers i VideoToolbox, Android ma SurfaceView i MediaCodec, a Linux ma VASurfaces i VA-API. Abstrakcje Chromium dotyczące tych pojęć są obsługiwane odpowiednio przez interfejsy OverlayProcessor i mojo::VideoDecoder.
W niektórych przypadkach te bufory mogą być mapowane do pamięci systemowej, więc nie muszą być nieprzezroczyste i nie zużywają żadnej przepustowości, dopóki nie zostaną zamapowane. Chromium nazywa je GpuMemoryBuffers. W systemie Windows są one obsługiwane przez bufory DXGI, w macOS przez IOSurfaces, w Androidzie przez AHardwareBuffers, a w Linuksie przez bufory DMA. Odtwarzanie filmów zwykle nie wymaga tego dostępu, ale te bufory są ważne dla przechwytywania filmów, ponieważ zapewniają minimalną przepustowość między urządzeniem do przechwytywania a ewentualnymi koderami.
Ponieważ GPU często odpowiada zarówno za dekodowanie, jak i wyświetlanie, korzystanie z tych (często nieprzezroczystych) buforów zapewnia, że dane wideo o dużej przepustowości nigdy nie opuszczają GPU. Jak już wspomnieliśmy, przechowywanie danych na karcie graficznej ma ogromne znaczenie dla wydajności, zwłaszcza przy wysokich rozdzielczościach i częstotliwościach klatek.
Im więcej możemy wykorzystać prymitywnych funkcji systemu operacyjnego, takich jak nakładki i bufory GPU, tym mniej przepustowości zużywa się na niepotrzebne przenoszenie bajtów wideo. Przechowywanie wszystkiego w jednym miejscu, od dekodowania po renderowanie, może przynieść niesamowitą wydajność energetyczną. Na przykład, gdy w Chromium włączono nakładki w systemie macOS, zużycie energii podczas odtwarzania filmów w trybie pełnoekranowym zmniejszyło się o połowę. Na innych platformach, takich jak Windows, Android i ChromeOS, możemy używać nakładek nawet w przypadkach innych niż pełnoekranowe, co pozwala zaoszczędzić nawet 50% zasobów.
renderowanie,
Teraz, gdy omówiliśmy już optymalne mechanizmy dostarczania, możemy omówić, jak Chromium wybiera, co ma dostarczyć. Pakiet odtwarzania Chromium korzysta z architektury opartej na „wyciąganiu”, co oznacza, że każdy komponent w pakiecie prosi o dane od komponentu znajdującego się niżej w hierarchii. Na szczycie stosu znajduje się renderowanie klatek dźwięku i obrazu, poniżej dekodowanie, a na końcu wejście/wyjście. Każda wyrenderowana klatka dźwięku przesuwa zegar, który służy do wybierania klatek wideo do renderowania w połączeniu z interwałem prezentacji.
W każdym interwale wyświetlania (każdym odświeżeniu wyświetlacza) renderer wideo otrzymuje od komponentu CompositorFrameSink, który jest dołączony do wspomnianego wcześniej SurfaceLayer, prośbę o przekazanie ramki wideo. W przypadku treści z częstotliwością klatek mniejszą niż częstotliwość wyświetlania oznacza to wyświetlanie tej samej klatki więcej niż raz, a jeśli częstotliwość klatek jest większa niż częstotliwość wyświetlania, niektóre klatki nie są wyświetlane.
Synchronizacja obrazu i dźwięku w sposób przyjemny dla widzów to jednak znacznie więcej. Więcej informacji o tym, jak uzyskać płynne odtwarzanie filmów w Chromium, znajdziesz w Project Butter. Wyjaśnia on, jak renderowanie filmu można podzielić na idealne sekwencje przedstawiające, ile razy powinna być wyświetlana każda klatka. Na przykład: „1 klatka co każde wyświetlanie ([1], 60 fps przy 60 Hz)”, „1 klatka co 2 interwały ([2], 30 fps przy 60 Hz)” lub bardziej skomplikowane wzorce takie jak [2:3:2:3:2] (25 fps przy 60 Hz), obejmujące wiele różnych klatek i interwałów wyświetlania. Im bardziej kodeki wideo będą trzymać się tego wzoru, tym płynniejsze będzie odtwarzanie.
Chociaż większość platform Chromium renderuje poszczególne klatki, nie wszystkie tak robią. Nasza rozszerzalna architektura umożliwia też renderowanie zbiorcze. Renderowanie zbiorcze to technika zwiększająca wydajność, w której kompozytor na poziomie systemu operacyjnego otrzymuje informacje o wielu klatkach z wyprzedzeniem i zarządza ich publikowaniem zgodnie z harmonogramem aplikacji.
Przyszłość to teraz?
Skupiliśmy się na tym, jak Chromium wykorzystuje podstawowe funkcje systemu operacyjnego, aby zapewnić najlepsze w swojej klasie odtwarzanie. A co z witrynami, które oferują więcej niż tylko podstawowe odtwarzanie filmów? Czy możemy zaoferować im te same zaawansowane prymitywy, których używa Chromium, aby wprowadzić nową generację treści internetowych?
Uważamy, że odpowiedź brzmi: tak. Rozszerzalność jest obecnie podstawą naszej koncepcji platformy internetowej. Współpracowaliśmy z innymi przeglądarkami i programistami nad nowymi technologiami, takimi jak WebGPU i WebCodecs, aby programiści internetowi mogli korzystać z tych samych prymitywów, których używa Chromium, podczas komunikacji z systemem operacyjnym. WebGPU zapewnia obsługę buforów GPU, a WebCodecs zapewnia prymitywy kodowania i dekodowania na platformie, które są zgodne z wspomnianymi systemami nakładek i buforów GPU.
Koniec transmisji
Dziękujemy za uwagę! Mam nadzieję, że po przeczytaniu tego artykułu lepiej rozumiesz, jak działają nowoczesne systemy odtwarzania i dlaczego Chromium umożliwia codzienne oglądanie setek milionów godzin treści. Jeśli chcesz dowiedzieć się więcej o kodekach i nowoczesnych filmach internetowych, polecamy artykuły H.264 is magic (Sid Bala), How Modern Video Players Work (Erica Beaves) oraz Packaging award-winning shows with award-winning technology (Cyril Concolato).
Ilustracja (ładna!) autorstwa Una Kravets.