Jestem Dale Curtis i jestem kierownikiem zespołu inżynierskiego ds. odtwarzania multimediów w Chromium. Mój zespół odpowiada za internetowe interfejsy API do odtwarzania filmów, takie jak MSE i WebCodecs, a także za odpowiednie rozwiązania wewnętrzne platformy używane do demuksowania, dekodowania i renderowania dźwięku i obrazu.
W tym artykule przedstawię architekturę renderowania wideo w Chromium. Choć niektóre szczegóły dotyczące rozszerzalności są prawdopodobnie związane z Chromium, większość omówionych tu koncepcji i projektów ma zastosowanie do innych silników renderowania, a nawet natywnych aplikacji do odtwarzania treści.
Architektura odtwarzania w Chromium znacznie się zmieniła na przestrzeni lat. Choć nie zaczęliśmy od koncepcji piramidy sukcesu, tak jak to opisujemy w pierwszym poście z tej serii, ostatecznie omówiliśmy podobne rozwiązania: niezawodność, wydajność i elastyczność.
Na początku renderowanie filmów było bardzo proste – tylko pętla polegała na wyborze dekodowanych przez oprogramowanie klatek wideo, które mają być przesłane do kompozytora. Przez lata było to dość niezawodne, ale wraz ze wzrostem złożoności internetu zapotrzebowanie na większą wydajność i wydajność doprowadziło do zmian architektury. Wiele ulepszeń wymagało zastosowania prymitywów specyficznych dla danego systemu operacyjnego. Dlatego też nasza architektura musiała być bardziej rozszerzalna, aby można było obsługiwać wszystkie platformy Chromium.
Renderowanie wideo można podzielić na 2 etapy: wybrać, co ma być dostarczone, i sprawnie dostarczać te informacje. Aby zwiększyć czytelność, zajmę się sprawnym przesyłaniem treści, a potem zajmę się sposobem, w jaki Chromium wybiera treści do wyświetlenia.
Niektóre terminy i układ
W tym artykule skupię się na renderowaniu, więc zajmę się tylko krótko tymi aspektami potoku.
Dekodowanie i demodyfikacja w naszym nowoczesnym świecie, w którym zwraca się uwagę na bezpieczeństwo, wymaga sporej ostrożności. Parsery binarne to rozbudowane środowiska docelowe, a odtwarzanie multimediów wymaga analizy binarnej. W związku z tym bardzo powszechne są problemy z zabezpieczeniami w parserach multimediów.
Chromium stosuje gruntowną ochronę, by zmniejszyć ryzyko wystąpienia problemów z bezpieczeństwem u naszych użytkowników. W praktyce oznacza to, że demuksowanie i dekodowanie oprogramowania zawsze odbywa się w ramach procesu o niskich uprawnieniach, natomiast dekodowanie sprzętowe odbywa się w procesie, który zapewnia jedynie odpowiednie uprawnienia do komunikacji z GPU systemu.
Mechanizm komunikacji międzyprocesowej w Chromium nazywa się Mojo. Nie będziemy omawiać szczegółowo Mojo w tym artykule, ale jako warstwa abstrakcji między procesami jest to podstawa szerokiego potoku multimediów w Chromium. Trzeba o tym pamiętać, przechodząc przez proces odtwarzania, ponieważ wpływa to na złożoną administrację komponentami w różnych procesach, które współdziałają, aby odbierać, demuksować, dekodować i w końcu wyświetlać multimedia.
Tyle różnych elementów
Zrozumienie współczesnych procesów renderowania wideo wymaga wiedzy o tym, dlaczego filmy są wyjątkowe: przepustowość. Odtwarzanie w rozdzielczości 3840 x 2160 (4K) przy 60 klatkach na sekundę wymaga 9–12 gigabitów/s przepustowości pamięci. Szczytowa przepustowość systemów w nowoczesnych systemach może wynosić setki gigabitów na sekundę, ale to spora część odtwarzania filmów. Bez ostrożności łączna przepustowość może łatwo się pomnożyć z powodu kopiowania lub przesyłania między GPU i pamięcią procesora.
Celem każdego nowoczesnego mechanizmu odtwarzania wideo, który ma na celu zapewnienie wydajności, jest zminimalizowanie przepustowości między dekoderem a ostatnim etapem renderowania. Z tego powodu renderowanie filmów jest w dużej mierze odłączone od głównego potoku renderowania Chromium. Z perspektywy naszego głównego procesu renderowania film jest po prostu otworem o stałym rozmiarze i przezroczystością. Chromium realizuje to dzięki koncepcji przestrzeń, w której każdy film jest bezpośrednio komunikowany z Viz.
Ze względu na popularność komputerów mobilnych moc i wydajność stały się ważnym elementem obecnej generacji. W efekcie dekodowanie i renderowanie są ściślej sprzężone niż kiedykolwiek wcześniej na poziomie sprzętu. W efekcie film wygląda jak dziura z przezroczystością, nawet z samym systemem operacyjnym. Dekodery na poziomie platformy często udostępniają nieprzezroczyste bufory, które Chromium przekazuje do systemu komponowania na poziomie platformy w postaci nakładek.
Każda platforma ma własną formę nakładek, z którymi współdziałają interfejsy API dekodowania platformy. W systemie Windows dostępne są funkcje Direct Composition i Media Foundation Transforms, macOS: CoreAnimation Warstwy i VideoToolbox, Android – SurfaceView i MediaCodec, a w systemie Linux VASurfaces i VA-API. Abstrakcje Chromium dla tych pojęć są obsługiwane odpowiednio przez interfejsy OverlayProcessor i mojo::VideoDecoder.
W niektórych przypadkach można mapować te bufory na pamięć systemową. Dzięki temu nie muszą być one nieprzezroczyste ani nie wykorzystują żadnej przepustowości, dopóki nie zostaną udostępnione w Chromium – w Chromium występuje on tzw. GpuMemoryBuffers. W systemie Windows używają buforów DXGI, IOSurfaces z macOS, AHardwareBuffers na Androidzie i buforów DMA w systemie Linux. Choć przy odtwarzaniu filmów ten dostęp zwykle nie jest wymagany, te bufory są ważne przy nagrywaniu wideo, ponieważ zapewniają minimalną przepustowość między urządzeniem rejestrującym a ewentualnymi koderami.
GPU jest często odpowiedzialne zarówno za dekodowanie, jak i wyświetlanie, dlatego korzystanie z tych (często) nieprzezroczystych buforów sprawia, że dane wideo o dużej przepustowości nigdy nie opuszczają GPU. Jak już wspomnieliśmy, przechowywanie danych w GPU jest niezwykle ważne dla wydajności, zwłaszcza przy wysokiej rozdzielczości i liczbie klatek.
Im bardziej będziemy mogli korzystać z elementów podstawowych systemu operacyjnego, takich jak nakładki i bufory GPU, tym mniej obciążamy łącza na tasowanie w obrębie niepotrzebnych bajtów wideo. Możliwość przechowywania w jednym miejscu wszystkiego, od dekodowania po renderowanie, może prowadzić do niesamowitej energooszczędności. Na przykład włączenie nakładek w systemie macOS powodowało zmniejszenie zużycia energii podczas odtwarzania filmów na pełnym ekranie. Na innych platformach, takich jak Windows, Android i ChromeOS, możemy używać nakładek nawet w przypadkach niepełnoekranowych, co pozwala zaoszczędzić prawie 50%.
renderowanie,
Omówiliśmy już optymalne mechanizmy dostarczania treści, możemy więc przejść do tego, jak Chromium wybiera treści do wyświetlenia. Stos odtwarzania w Chromium korzysta z architektury opartej na „pobieraniu”, co oznacza, że każdy komponent stosu żąda danych wejściowych od elementu znajdującego się pod nim w kolejności hierarchicznej. Na szczycie stosu znajduje się renderowanie klatek audio i wideo, kolejny etap to dekodowanie, demuksowanie, a na koniec we/wy. Każda wyrenderowana ramka audio powoduje przesunięcie zegara o klatki wideo do renderowania połączone z interwałem prezentacji.
Przy każdym odstępie między prezentacją (przy każdym odświeżeniu wyświetlacza) mechanizm renderowania jest proszony o dostarczenie klatki wideo za pomocą obiektu CompositorFrameSink dołączonego do wspomnianej wcześniej warstwy SurfaceLayer. W przypadku treści z liczbą klatek mniejszą niż liczba wyświetlania oznacza to, że ta sama klatka została wyświetlona więcej niż raz, a jeśli liczba klatek jest większa niż liczba wyświetlanych, niektóre klatki nigdy nie są wyświetlane.
Jest to o wiele więcej do synchronizowania dźwięku z obrazem w sposób, który spodoba się widzom. Więcej informacji o zapewnianiu optymalnej płynności filmów w Chromium znajdziesz w artykule Project Butter. Wyjaśniamy, jak można podzielić renderowanie filmów na idealne sekwencje reprezentujące liczbę wyświetleń każdej klatki. Na przykład: „1 klatka na każdy interwał wyświetlania ([1], 60 kl./s przy 60 Hz), „1 klatka co 2 odstępy ([2], 30 kl./s przy 60 Hz)” lub bardziej skomplikowane wzorce, takie jak [2:3:2:3:2] (25 klatek na sekundę w 60 klatkach na 60 klatek) Im ściślej mechanizm renderowania wideo trzyma się tego idealnego wzorca, tym większe prawdopodobieństwo, że użytkownik uzna, że odtwarzanie jest płynne.
Większość platform Chromium renderuje treść klatka po klatce, ale nie wszystkie. Nasza rozszerzalna architektura umożliwia też renderowanie wsadowe. Renderowanie wsadowe to technika zwiększająca wydajność, w ramach której kompozytor na poziomie systemu operacyjnego jest informowany o wielu klatkach z wyprzedzeniem i obsługuje publikowanie ich zgodnie z harmonogramem określonym przez aplikację.
Przyszłość należy do teraz?
Skupiliśmy się na tym, jak Chromium wykorzystuje podstawowe funkcje systemu operacyjnego, aby zapewnić najwyższą jakość odtwarzania. A co z witrynami, które chcą wyjść poza zwykłe odtwarzanie filmów? Czy możemy zaoferować im te same zaawansowane funkcje podstawowe, które sama Chromium wykorzystuje do zapoczątkowania nowej generacji treści internetowych?
Naszym zdaniem tak. Podstawą naszego myślenia o platformie internetowej jest obecnie rozszerzanie. Współpracujemy z innymi przeglądarkami i programistami nad tworzeniem nowych technologii, takich jak WebGPU i WebCodecs, dzięki którym deweloperzy stron internetowych mogą używać tych samych podstawowych elementów, które Chromium udostępnia podczas komunikacji z systemem operacyjnym. WebGPU obsługuje bufory GPU, a WebCodecs udostępnia podstawowe elementy do dekodowania i kodowania platformy zgodne ze wspomnianymi wcześniej systemami buforowania nakładki i GPU.
Koniec transmisji
Dziękujemy za uwagę! Mam nadzieję, że udało Ci się dowiedzieć więcej o nowoczesnych systemach odtwarzania i o tym, jak Chromium zapewnia codziennie kilkaset milionów godzin czasu oglądania. Jeśli szukasz więcej informacji o kodekach i nowoczesnym filmie internetowym, polecam: H.264 to magic autorstwa Sid Baly, How Modern Video Players Work autorstwa Erica Beaves i Wielokrotnie nagradzane programy z nagradzaną technologią Cyrila Concolato.
Jedna ilustracja (ładna!) autorstwa Uny Kravets.