Ik ben Dale Curtis, technisch hoofd voor het afspelen van media in Chromium. Mijn team is verantwoordelijk voor de webgerichte API's voor het afspelen van video, zoals MSE en WebCodecs , en de platformspecifieke interne onderdelen die betrokken zijn bij het demuxen, decoderen en weergeven van audio en video.
In dit artikel begeleid ik je door de videorenderingarchitectuur van Chromium. Hoewel sommige details rond uitbreidbaarheid waarschijnlijk Chromium-specifiek zijn, zijn de meeste concepten en ontwerpen die hier worden besproken van toepassing op andere rendering-engines en zelfs native afspeel-apps.
De afspeelarchitectuur van Chromium is in de loop der jaren aanzienlijk veranderd. Hoewel we niet begonnen met het idee van een succespiramide , zoals beschreven in het eerste bericht van deze serie, volgden we uiteindelijk vergelijkbare stappen: betrouwbaarheid, prestaties en vervolgens uitbreidbaarheid.
In het begin was het renderen van video vrij eenvoudig : slechts een for-loop waarin werd gekozen welke software de videoframes had gedecodeerd om naar de compositor te sturen. Jarenlang was dit betrouwbaar genoeg, maar naarmate de complexiteit van het web toenam, leidde de behoefte aan meer prestaties en efficiëntie tot architectonische veranderingen. Voor veel verbeteringen waren besturingssysteemspecifieke primitieven nodig; Daarom moest onze architectuur ook beter uitbreidbaar worden om alle Chromium-platforms te bereiken.
Videoweergave kan in twee stappen worden opgesplitst: kiezen wat u wilt leveren en die informatie efficiënt leveren. In het belang van de leesbaarheid bespreek ik de efficiënte bezorging voordat ik inga op de manier waarop Chromium kiest wat te leveren.
Enkele termen en lay-out
Omdat dit artikel zich richt op weergave, zal ik slechts kort ingaan op de demux- en decoderingsaspecten van de pijplijn.
Het decoderen en demuxen in onze moderne, veiligheidsbewuste wereld vereist behoorlijk wat zorg. Binaire parsers zijn rijke doelomgevingen en het afspelen van media zit vol met binaire parsering. Als zodanig komen beveiligingsproblemen bij mediaparsers zeer vaak voor.
Chromium oefent een diepgaande verdediging uit om het risico op beveiligingsproblemen voor onze gebruikers te verminderen. In praktische termen betekent dit dat demuxing en softwaredecodering altijd plaatsvinden in een proces met weinig privileges, terwijl hardwaredecodering plaatsvindt in een proces met net voldoende privileges om met de GPU van het systeem te praten.
Het procesoverschrijdende communicatiemechanisme van Chromium heet Mojo . Hoewel we in dit artikel niet op de details van Mojo zullen ingaan, is Mojo, als abstractielaag tussen processen, een hoeksteen van de uitbreidbare mediapijplijn van Chromium. Het is belangrijk om je hiervan bewust te zijn terwijl we door de afspeelpijplijn lopen, omdat deze informatie geeft over de complexe orkestratie van cross-procescomponenten die samenwerken om media te ontvangen, demuxen, decoderen en uiteindelijk weer te geven.
Zoveel stukjes
Om de huidige videorenderingpijplijnen te begrijpen, is kennis nodig van waarom video speciaal is: bandbreedte. Bij afspelen met een resolutie van 3840x2160 (4K) en 60 frames per seconde wordt tussen de 9 en 12 gigabit/seconde aan geheugenbandbreedte gebruikt. Hoewel moderne systemen een piekbandbreedte van honderden gigabits per seconde kunnen hebben, vertegenwoordigt het afspelen van video nog steeds een aanzienlijk deel. Zonder zorg kan de totale bandbreedte gemakkelijk vermenigvuldigen als gevolg van kopieën of trips tussen GPU- en CPU-geheugen.
Het doel van elke moderne videoafspeelengine die efficiëntie in gedachten heeft, is het minimaliseren van de bandbreedte tussen de decoder en de laatste weergavestap. Om deze reden is videoweergave grotendeels losgekoppeld van de belangrijkste weergavepijplijn van Chromium. Concreet gezien is video, vanuit het perspectief van onze hoofdrenderingpijplijn, slechts een gat met een vaste grootte en ondoorzichtigheid. Chromium bereikt dit met behulp van een concept dat oppervlakken wordt genoemd, waarbij elke video rechtstreeks met Viz praat.
Vanwege de populariteit van mobiel computergebruik zijn kracht en efficiëntie een belangrijk aandachtspunt geworden voor de huidige generatie. Een resultaat hiervan is dat decodering en weergave op hardwareniveau meer dan ooit met elkaar zijn verbonden, waardoor video er gewoon uitziet als een gat met ondoorzichtigheid, zelfs voor het besturingssysteem zelf! Decoders op platformniveau bieden vaak alleen ondoorzichtige buffers die Chromium doorgeeft aan het compositiesysteem op platformniveau in de vorm van overlays.
Elk platform heeft zijn eigen vorm van overlays waarmee hun platformdecoderings-API's samenwerken. Windows heeft Direct Composition en Media Foundation Transforms , macOS heeft CoreAnimation Layers en VideoToolbox , Android heeft SurfaceView en MediaCodec , en Linux heeft VASurfaces en VA-API . De abstracties van Chromium voor deze concepten worden respectievelijk afgehandeld door de interfaces OverlayProcessor en mojo::VideoDecoder .
In sommige gevallen is het mogelijk dat deze buffers kunnen worden toegewezen aan het systeemgeheugen, zodat ze niet eens ondoorzichtig hoeven te zijn en geen bandbreedte verbruiken totdat ze worden benaderd. Chromium noemt deze GpuMemoryBuffers . Op Windows worden deze ondersteund door DXGI-buffers , op macOS IOSurfaces , op Android AHardwareBuffers en op Linux DMA-buffers . Hoewel het afspelen van video's deze toegang doorgaans niet nodig heeft, zijn deze buffers belangrijk voor video-opname om een minimale bandbreedte tussen het opnameapparaat en eventuele encoders te garanderen.
Omdat de GPU vaak verantwoordelijk is voor zowel decodering als weergave, zorgt het gebruik van deze (ook vaak) ondoorzichtige buffers ervoor dat videogegevens met hoge bandbreedte de GPU nooit verlaten. Zoals we eerder hebben besproken, is het bewaren van gegevens op de GPU ongelooflijk belangrijk voor de efficiëntie; vooral bij hoge resoluties en framesnelheden.
Hoe meer we kunnen profiteren van OS-primitieven zoals overlays en GPU-buffers, des te minder bandbreedte wordt besteed aan het onnodig schudden van videobytes. Door alles op één plek te houden, van het decoderen tot het renderen, kan dit leiden tot een ongelooflijke energie-efficiëntie. Toen Chromium bijvoorbeeld overlays op macOS inschakelde , werd het energieverbruik tijdens het afspelen van video op volledig scherm gehalveerd! Op andere platforms zoals Windows , Android en ChromeOS kunnen we overlays gebruiken, zelfs in gevallen die niet op volledig scherm zijn, waardoor bijna overal tot 50% wordt bespaard.
Weergave
Nu we de optimale leveringsmechanismen hebben besproken, kunnen we bespreken hoe Chromium kiest wat te leveren. De afspeelstapel van Chromium maakt gebruik van een op "pull" gebaseerde architectuur, wat betekent dat elke component in de stapel zijn invoer opvraagt van degene eronder in hiërarchische volgorde. Bovenaan de stapel staat de weergave van audio- en videoframes, daarna decodering, gevolgd door demuxen en ten slotte I/O. Bij elk weergegeven audioframe wordt een klok vooruitgezet die wordt gebruikt om videoframes te kiezen voor weergave in combinatie met een presentatie-interval.
Bij elk presentatie-interval (elke vernieuwing van het scherm) wordt de videorenderer gevraagd een videoframe te leveren door een CompositorFrameSink die is gekoppeld aan de eerder genoemde SurfaceLayer . Voor inhoud met een framesnelheid die lager is dan de weergavesnelheid betekent dit dat hetzelfde frame meer dan één keer wordt weergegeven, terwijl als de framesnelheid groter is dan de weergavesnelheid, sommige frames nooit worden weergegeven.
Er komt veel meer kijken bij het synchroniseren van audio en video op een manier die kijkers prettig vindt. Zie Project Butter voor een langere discussie over hoe optimale videovloeiing wordt bereikt in Chromium. Er wordt uitgelegd hoe videoweergave kan worden opgesplitst in ideale reeksen die aangeven hoe vaak elk frame moet worden weergegeven. Bijvoorbeeld: "1 frame per weergave-interval ([1], 60 fps in 60 Hz)", "1 frame per 2 intervallen ([2], 30 fps in 60 Hz)", of ingewikkelder patronen zoals [2:3 :2:3:2] (25 fps in 60 Hz) voor meerdere afzonderlijke frames en weergave-intervallen. Hoe dichter een videorenderer bij dit ideale patroon blijft, hoe groter de kans dat een gebruiker het afspelen als vloeiend ervaart.
Hoewel de meeste Chromium-platforms frame voor frame worden weergegeven, doen ze dat niet allemaal. Onze uitbreidbare architectuur maakt ook batchweergave mogelijk. Batched rendering is een efficiëntietechniek waarbij de compositor op besturingssysteemniveau vooraf op de hoogte wordt gesteld van meerdere frames en deze afhandelt volgens een door de toepassing verstrekt tijdschema.
De toekomst is nu?
We hebben ons geconcentreerd op de manier waarop Chromium profiteert van OS-primitieven om de beste afspeelervaring in zijn klasse te bieden. Maar hoe zit het met websites die verder willen gaan dan het eenvoudig afspelen van video's? Kunnen we hen dezelfde krachtige primitieven aanbieden die Chromium zelf gebruikt om de volgende generatie webinhoud in te luiden?
Wij denken dat het antwoord ja is! Uitbreidbaarheid vormt de kern van hoe we tegenwoordig over het webplatform denken. We hebben met andere browsers en ontwikkelaars samengewerkt om nieuwe technologieën zoals WebGPU en WebCodecs te creëren, zodat webontwikkelaars precies dezelfde primitieven kunnen gebruiken die Chromium gebruikt als ze met het besturingssysteem praten. WebGPU biedt ondersteuning voor GPU-buffers en WebCodecs biedt platformdecodering en codering van primitieven die compatibel zijn met de bovengenoemde overlay- en GPU-buffersystemen.
Einde stroom
Bedankt voor het lezen! Ik hoop dat je een beter begrip hebt gekregen van moderne afspeelsystemen en hoe Chromium elke dag honderden miljoenen uren aan kijktijd mogelijk maakt. Als je meer wilt lezen over codecs en moderne webvideo, raad ik je aan H.264 is magic van Sid Bala, How Modern Video Players Work van Erica Beaves en het verpakken van bekroonde shows met bekroonde technologie van Cyril Concolato.
Eén illustratie (de mooie!) van Una Kravets.