Seberapa sulit “memainkan video” di internet? Well:

  • Container format: .mkv, .mp4, .mov, .ts, .3gp (ehm), .webm
  • Coding format: h.265 (HEVC), h.264 (AVC), vp9, av1
  • Playback: HTTP Live Streaming (HLS), Adaptive Bitrate Streaming (DASH?), Pseudo-streaming (mp4, webm)

Let’s say ingin memainkan video 5 Centimeters Per Second (2007) dengan spesifikasi:

  • Ukuran: 1.68 GB
  • Resolusi: 1440p (2560x1440, 2K)
  • Framerate: 60fps
  • Container: mkv

Kasarnya, untuk mengunduh 1.68 GB, dengan kecepatan internet 100 Mbps (12.5 MB/s) saja perlu waktu 135 detik (2m 15s) saat penonton menekan tombol klik. Tapi peramban tidak mengerti mkv, dan umumnya video player yang digunakan menggunakan pendekatan pseudo-streaming alias progressive download.

Transcoding diperlukan untuk:

  • Mengubah container mkv menjadi ke something yang dimengerti peramban (let’s say mp4)
  • Apa resolusi yang diinginkan (atau ideal) untuk penonton?

Untuk video 1440p@60 idealnya membutuhkan koneksi 24 Mbps (atau 66-85 Mbps untuk HDR), kasarnya, ukuran file per detiknya bisa dikatakan 3 MB. Jika penonton tidak memiliki bandwidth 24 Mbps, kemungkinan harus dilakukan downscaling yang anggap menjadi 1080p (FHD) atau 720p (HD).

Transcoding membutuhkan biaya, tentunya “the bigger the better”. Beberapa platform mendukung “Hardware accelerated video encoding” alias HVA yang mana proses transcoding dilakukan di GPU sehingga CPU tidak terlalu sibuk seperti tetap bisa men-deliver video ke penonton dengan tenang.

Tergantung platform yang digunakan, sejauh ini ada 4 cara yang bisa digunakan khususnya di OS berbasis GNU/Linux (come on, what else?):

Untuk keluarga “Apple Silicon” ini agak ribet, di Mac OS bisa menggunakan VideoToolbox dan untuk Asahi Linux, entahlah, tapi di mesin saya memiliki /dev/dri. Bagaimanapun saya belum terlalu familiar dengan HVA di keluarga aarch64.

Biaya untuk transcoding relatif tidak terlalu besar (sekalipun dilakukan di CPU) namun yang menjadi tantangan bila proses tersebut dilakukan on-the-fly terlebih melakukan downscaling ke resolusi yang bervariasi meskipun umumnya ke bentuk yang lebih kecil dari yang asli.

Anggap video 5 Centimeters Per Second tersebut saya mainkan di 720p@60 dalam container/format mp4, si server harus bisa melakukan transcoding lebih cepat dari yang diminta oleh si penonton, plus harus bisa men-deliver nya secara instan juga. Jika tidak, sesuatu yang disebut “buffering” akan terjadi, karena… terjadi buffer.

Hal vital lain selain transcoding adalah encoding, ini tentang meng-kompresi video menjadi bentuk yang berukuran lebih “ramping” dari yang asli. Jika pernah mendengar kata “lossless” ataupun “lossy”, kemungkinan yang dibahasa adalah tentang encoding (atau compression pada umumnya).

Saya belum memiliki pengetahuan lebih dalam tentang ini, yang jelas pasti ada tradeoff khususnya di kualitas atau apapun itu yang nantinya akan ditampilkan dan didengar.

Bentuk asli dari video diatas adalah .mov (QuickTime) untuk container nya dengan ukuran ~4 GB. Tanpa transcoding, video tersebut tidak bisa dimainkan khususnya di peramban dan tanpa encoding, video tersebut tidak akan berukuran 2.3 GB untuk 1080p dan 1.2 GB untuk 720p.

Peer-to-Peer

Jika sudah familiar dengan kata “seeders” dan “peers”, well, tidak perlu penjelasan lebih detail untuk ini :)

Jika belum, mari kita buat diagram. Umumnya di arsitektur client-server, diagramnya adalah seperti ini:

flowchart LR

p1((client)) --> P[server]

P --> p1

Client (penonton) meminta file, server memberi file. Dalam konteks disini, file tersebut adalah video.

Jika ada 3 client yang sedang terhubung bersamaan, gambarnya seperti ini:

flowchart LR

p1((client1)) --> P[server]

P --> p1

  

p2((client2)) --> P

P --> p2((client2))

  

p3((client3)) --> P

P --> p3((client3))

Sangat standar. Lihat bagaimana server terlihat overwhelming mengatur request masuk dan memberikan response kepada client. Pengukurnya adalah seberapa banyak server dapat meng-handle penonton dalam waktu bersamaan dan berbicara tentang skalabilitas, umumnya menggunakan pendekatan scale-out dengan cara menambahkan server lagi:

flowchart LR

p1((client1)) --> P[server]

P --> p1

  

p2((client2)) --> P

P --> p2((client2))

  

p3((client3)) --> P9[server2]

P9 --> p3((client3))

Di real-world case seperti ini:

flowchart LR

p1((client1)) --> LB[Load Balancer]

p2((client2)) --> LB

p3((client3)) --> LB

  

LB .-> server1

LB .-> server2

LB .-> server1

  

server1 .-> LB

server2 .-> LB

server1 .-> LB

  

LB --> p1

LB --> p2

LB --> p3

Terlihat bila beban “Load Balancer” menjadi overwhelming namun tenang itu memang sudah tugas doi.

Dengan teknologi peer-to-peer (p2p), hal seperti ini adalah hal yang mungkin:

flowchart LR

p1((client1)) --> P[server]

  

P --> p1

  

p2((client2)) --> P

P --> p2((client2))

  

p3((client3)) .-> p2

p2 .-> p3

  

p4((client4)) .-> p1

p1 .-> p4

Dengan catatan:

  • client1 dan client4 menonton video yang sama dengan resolusi yang sama
  • client2 dan client3 menonton video yang sama dengan resolusi yang sama
  • Timestamp client4 dibelakang timestamp client1
  • Timestamp client3 dibelakang timestamp client2

Dari diagram diatas, client1 dan client2 technically menjadi “seeder” yang mana client3 dan client4 menjadi “peer”. Teknologi ini dapat mengurangi beban kerja server sekaligus mengurangi penggunaan bandwidth yang mahal itu.

Apa yang terjadi saat client1 atau client2 tidak dapat dihubungi (menjadi “leecher”, sad)? Sederhana, client3 dan client2 akan terhubung langsung ke server and that’s it.

Jika timestamp client4 lebih depan dari client1 seperti melakukan seeking, well, permintaan akan dijadikan ke server seperti ini:

flowchart LR

p1((client1)) --> P[server]

  

P --> p1

  

p2((client2)) --> P

P --> p2((client2))

  

p3((client3)) .-> p2

p2 .-> p3

  

p4((client4)) --> P

P --> p4

Dan tidak menutup kemungkinan kondisinya berubah menjadi seperti ini:

flowchart LR

p1((client1)) .-> p4

  

p4 .-> p1

  

p2((client2)) --> P

P --> p2((client2))

  

p3((client3)) .-> p2

p2 .-> p3

  

p4((client4)) --> P[server]

P --> p4

Yeah hidup kadang jadi seeder kadang jadi leecher.

Dalam dunia per-video-an, p2p bukanlah teknologi yang baru. Teknologi inilah yang digunakan dalam Real-time Communication (RTC) seperti panggilan suara dan video yang dilakukan melalui jaringan internet. Dalam penerapannya, tentu tidak mudah. Dan anggap saya sedang tidak mood membahas tentang Network Address Translation (NAT).

Teknologi p2p ini digunakan di platform berbagi video PeerTube, dan tidak hanya untuk VoD, melainkan untuk Live Video juga, which is nice.

Transcoding vs Transmuxing

Di awal kita sempat membahas tentang “Coding Format” yang umumnya digunakan bebarengan dengan istilah “Codecs” alias Compress/Decompress. Juga, tentang “Container Format”, alias tentang “pembungkus” si video.

Membahas tentang kompatibilitas, lapisan nya adalah seperti ini:

  • Container berisi “video”, “audio”, dan “subtitle”
  • Server perlu tahu:
    • Apakah si client/video player dapat “memulai langsung” si video yang ada di server, yang berarti, si client tersebut harus mendukung container dari video tersebut
    • Apakah si client tersebut mendukung codecs yang digunakan. Video/audio bisa menggunakan codecs yang beragam, misal video menggunakan h.265 dan audio menggunakan DTS
    • Apakah si client tersebut mampu meng-handle bitrate yang dimiliki melalui koneksi jaringan yang digunakan
  • Jika ketiga hal diatas terpenuhi, tidak ada hal rumit yang dilakukan alias “direct play” dapat terjadi
  • Jika si client mendukung container namun tidak mendukung codecs yang digunakan, maka transcoding akan terjadi
  • Jika si client mendukung codecs namun tidak dengan container, maka transmuxing (remux?) akan terjadi
  • Jika si client mendukung keduanya namun tidak dengan bitrate, maka transcoding akan terjadi (ke resolusi yang lebih rendah)

Proses transmuxing (hanya mengubah container, tanpa menyentuh video/audio) umumnya lebih ringan daripada transcoding. Transmuxing mungkin lebih berguna untuk Live Streaming kecuali bila berurusan dengan bitrate yang perlu melakukan transcoding.

On-the-fly vs On-demand transcoding

Di PeerTube, proses transcoding terjadi setelah pengguna mengunggah video (on-demand since selalu mengarah ke konfigurasi resolusi). Multiplier nya adalah daftar resolusi yang didukung, sehingga total penyimpanan untuk satu video akan selalu beberapa kali lebih besar tergantung berapa resolusi yang didukung oleh si admin dari instance tersebut.

Di beberapa tempat lain, terkadang transcoding dilakukan on-the-fly, Jellyfin salah satunya:

Disitu terlihat bahwa container—somehow dilaporin sebagai “mkv” dibagian container Original Media Info yang padahal aslinya “webm” alias vp9—dan video codec dan audio codec yang dimiliki si file tersebut tidak didukung oleh si player. Satu-satunya cara—per-paragraf ini ditulis—untuk direct play di Jellyfin iOS adalah:

  • Container: mp4/mov/ts
  • Video codec: h.264 8-bit
  • Audio codec: flac/mp3/aac

Jellyfin iOS pada dasarnya hanyalah a fucking webview. Gunakan Swiftin.

Berikut bila diakses melalui Mozilla Firefox yang berjalan di Steam Deck saya (GNU/Linux):

Terlihat bahwa “play method” nya Direct Playing karena Mozilla Firefox di tempat saya mendukung container webm dan video codec vp9 dan audio codec OPUS.

Video di-deliver as-is.