Saya Dale Curtis, pemimpin engineer untuk pemutaran media di Chromium. Tim saya bertanggung jawab atas API yang ditampilkan di web untuk pemutaran video seperti MSE dan WebCodecs, serta internal khusus platform yang terlibat dalam demuxing, decoding, dan rendering audio dan video.
Dalam artikel ini, saya akan memandu Anda mempelajari arsitektur rendering video Chromium. Meskipun beberapa detail seputar ekstensi mungkin khusus Chromium, sebagian besar konsep dan desain yang dibahas di sini berlaku untuk mesin rendering lain dan bahkan aplikasi pemutaran native.
Arsitektur pemutaran Chromium telah berubah secara signifikan selama bertahun-tahun. Meskipun kita tidak memulai dengan ide piramida kesuksesan seperti yang dijelaskan dalam postingan pertama dalam seri ini, pada akhirnya kita mengikuti langkah-langkah yang serupa: keandalan, performa, lalu ekstensi.
Pada awal, rendering video cukup sederhana—hanya loop for yang memilih software yang mendekode frame video untuk dikirim ke kompositor. Selama bertahun-tahun, hal ini cukup andal, tetapi seiring meningkatnya kompleksitas web, kebutuhan akan performa dan efisiensi yang lebih tinggi menyebabkan perubahan arsitektur. Banyak peningkatan yang memerlukan primitif khusus OS; jadi, arsitektur kami juga harus menjadi lebih dapat diperluas untuk menjangkau semua platform Chromium.
Rendering video dapat dibagi menjadi dua langkah: memilih apa yang akan dikirimkan dan mengirimkan informasi tersebut secara efisien. Untuk memudahkan pembacaan, Saya akan membahas pengiriman yang efisien sebelum membahas cara Chromium memilih apa yang akan dikirim.
Beberapa istilah dan tata letak
Karena artikel ini berfokus pada rendering, saya hanya akan membahas aspek demuxing dan decoding pipeline secara singkat.
Dekode dan demuxing di dunia modern yang mengutamakan keamanan memerlukan perhatian yang cukup. Parser biner adalah lingkungan target yang kaya, dan pemutaran media penuh dengan penguraian biner. Dengan demikian, masalah keamanan di parser media sangat umum.
Chromium menerapkan defense in depth untuk mengurangi risiko masalah keamanan bagi pengguna kami. Secara praktis, hal ini berarti demuxing dan decoding software selalu terjadi dalam proses dengan hak istimewa rendah, sedangkan decoding hardware terjadi dalam proses dengan hak istimewa yang cukup untuk berkomunikasi dengan GPU sistem.
Mekanisme komunikasi lintas proses Chromium disebut Mojo. Meskipun kita tidak akan membahas detail Mojo dalam artikel ini, sebagai lapisan abstraksi di antara proses, Mojo adalah fondasi pipeline media yang dapat diperluas di Chromium. Penting untuk mengetahui hal ini saat kita membahas pipeline pemutaran karena hal ini menginformasikan orkestrasi kompleks komponen lintas proses yang berinteraksi untuk menerima, demux, mendekode, dan akhirnya menampilkan media.
Begitu banyak bit
Untuk memahami pipeline rendering video saat ini, Anda memerlukan pengetahuan tentang alasan video menjadi spesial: bandwidth. Pemutaran resolusi 3840x2160 (4K) dengan 60 frame per detik menggunakan bandwidth memori antara 9-12 gigabit/detik. Meskipun sistem modern mungkin memiliki bandwidth puncak dalam ratusan gigabit per detik, pemutaran video masih mewakili sebagian besar bandwidth. Tanpa perawatan, total bandwidth dapat dengan mudah berlipat ganda karena salinan atau perjalanan antara memori GPU dan CPU.
Sasaran mesin pemutaran video modern dengan efisiensi adalah meminimalkan bandwidth antara dekoder dan langkah rendering akhir. Karena alasan ini, rendering video sebagian besar dipisahkan dari pipeline rendering utama Chromium. Secara khusus, dari perspektif pipeline rendering utama kami, video hanyalah lubang berukuran tetap dengan opasitas. Chromium mencapai hal ini menggunakan konsep yang disebut platform—dengan setiap video berkomunikasi langsung dengan Viz.
Karena popularitas komputasi seluler, kekuatan dan efisiensi telah menjadi fokus yang signifikan di generasi saat ini. Akibatnya, decoding dan rendering lebih terikat daripada sebelumnya di tingkat hardware—sehingga video hanya terlihat seperti lubang dengan opasitas, bahkan untuk OS itu sendiri. Dekoder tingkat platform sering kali hanya menyediakan buffering buram yang diteruskan Chromium ke sistem komposisi tingkat platform dalam bentuk overlay.
Setiap platform memiliki bentuk overlay sendiri yang bekerja sama dengan API decoding platform mereka. Windows memiliki Direct Composition dan Media Foundation Transforms, macOS memiliki CoreAnimation Layers dan VideoToolbox, Android memiliki SurfaceView dan MediaCodec, dan Linux memiliki VASurfaces dan VA-API. Abstraksi Chromium untuk konsep ini ditangani oleh antarmuka OverlayProcessor dan mojo::VideoDecoder.
Dalam beberapa kasus, buffer ini dapat dipetakan ke dalam memori sistem, sehingga tidak perlu buram dan tidak menggunakan bandwidth apa pun hingga diakses—Chromium menyebutnya GpuMemoryBuffers. Di Windows, hal ini didukung oleh buffer DXGI, di macOS IOSurfaces, di Android AHardwareBuffers, dan di Linux buffer DMA. Meskipun pemutaran video umumnya tidak memerlukan akses ini, buffer ini penting untuk perekaman video guna memastikan bandwidth minimal antara perangkat perekaman dan encoder akhir.
Karena GPU sering kali bertanggung jawab untuk decoding dan menampilkan, penggunaan buffering buram (juga sering) ini memastikan bahwa data video dengan bandwidth tinggi tidak pernah benar-benar keluar dari GPU. Seperti yang telah kita bahas sebelumnya, menyimpan data di GPU sangat penting untuk efisiensi; terutama pada resolusi dan kecepatan frame tinggi.
Makin banyak kita dapat memanfaatkan primitif OS seperti overlay dan buffering GPU, makin sedikit bandwidth yang dihabiskan untuk mengacak byte video yang tidak perlu. Menyimpan semuanya di satu tempat mulai dari decoding hingga rendering dapat menghasilkan efisiensi daya yang luar biasa. Misalnya, saat Chromium mengaktifkan overlay di macOS, konsumsi daya selama pemutaran video layar penuh berkurang setengahnya. Di platform lain seperti Windows, Android, dan ChromeOS, kita dapat menggunakan overlay bahkan dalam kasus non-layar penuh, sehingga menghemat hingga 50% di hampir semua tempat.
Rendering
Setelah membahas mekanisme pengiriman yang optimal, kita dapat membahas cara Chromium memilih apa yang akan dikirim. Stack pemutaran Chromium menggunakan arsitektur berbasis "pull", yang berarti setiap komponen dalam stack meminta inputnya dari komponen di bawahnya dalam urutan hierarkis. Di bagian atas stack adalah rendering frame audio dan video, di bawahnya adalah decoding, diikuti dengan demuxing, dan terakhir I/O. Setiap frame audio yang dirender akan memajukan jam yang digunakan untuk memilih frame video untuk rendering saat digabungkan dengan interval presentasi.
Pada setiap interval presentasi (setiap refresh layar), perender video diminta untuk menyediakan frame video oleh CompositorFrameSink yang dilampirkan ke SurfaceLayer yang disebutkan sebelumnya. Untuk konten dengan frekuensi frame yang lebih kecil dari frekuensi tampilan, artinya menampilkan frame yang sama lebih dari sekali, sedangkan jika frekuensi frame lebih besar dari frekuensi tampilan, beberapa frame tidak pernah ditampilkan.
Ada banyak hal lain yang perlu dilakukan untuk menyinkronkan audio dan video dengan cara yang menyenangkan bagi penonton. Lihat Project Butter untuk diskusi yang lebih panjang tentang cara mencapai kelancaran video yang optimal di Chromium. Artikel ini menjelaskan cara rendering video dapat dibagi menjadi urutan ideal yang mewakili frekuensi setiap frame harus ditampilkan. Misalnya: "1 frame setiap interval tampilan ([1], 60 fps dalam 60 Hz)", "1 frame setiap 2 interval ([2], 30 fps dalam 60 Hz)", atau pola yang lebih rumit seperti [2:3:2:3:2] (25 fps dalam 60 Hz) yang mencakup beberapa frame dan interval tampilan yang berbeda. Semakin dekat perender video dengan pola ideal ini, semakin besar kemungkinan pengguna akan merasakan pemutaran yang lancar.
Meskipun sebagian besar platform Chromium merender frame demi frame, tidak semuanya demikian. Arsitektur kami yang dapat diperluas juga memungkinkan rendering batch. Rendering batch adalah teknik efisiensi saat komposer tingkat OS diberi tahu tentang beberapa frame terlebih dahulu dan menangani peluncurannya pada jadwal pengaturan waktu yang disediakan aplikasi.
Masa depan sudah di depan mata?
Kami berfokus pada cara Chromium memanfaatkan primitif OS untuk memberikan pengalaman pemutaran terbaik di kelasnya. Namun, bagaimana dengan situs yang ingin melakukan lebih dari pemutaran video dasar? Dapatkah kita menawarkan primitif canggih yang sama dengan yang digunakan Chromium sendiri untuk menghadirkan konten web generasi berikutnya?
Kami rasa jawabannya adalah ya. Ekstensibilitas adalah inti dari cara kita memikirkan platform web saat ini. Kami telah bekerja sama dengan browser dan developer lain untuk membuat teknologi baru seperti WebGPU dan WebCodecs sehingga developer web dapat menggunakan primitif yang sama dengan yang dilakukan Chromium saat berkomunikasi dengan OS. WebGPU menghadirkan dukungan untuk buffer GPU dan WebCodecs menghadirkan primitif decoding dan encoding platform yang kompatibel dengan sistem buffer GPU dan overlay yang disebutkan di atas.
Akhir aliran
Terima kasih telah membaca. Semoga Anda memiliki pemahaman yang lebih baik tentang sistem pemutaran modern dan cara Chromium mendukung waktu tontonan beberapa ratus juta jam setiap hari. Jika Anda mencari bacaan lebih lanjut tentang codec dan video web modern, sebaiknya baca H.264 is magic oleh Sid Bala, How Modern Video Players Work oleh Erica Beaves, dan Packaging award-winning shows with award-winning technology oleh Cyril Concolato.
Satu ilustrasi (yang cantik!) oleh Una Kravets.