D
P
0
← Semua artikel Read in English

Layout, Overflow & Cascade CSS

Hasil `out.webm` Malah Lebih Besar dari `in.mp4`? CRF VP9 Itu Target Kualitas, Bukan Target Ukuran

· · 4 menit baca
Hasil `out.webm` Malah Lebih Besar dari `in.mp4`? CRF VP9 Itu Target Kualitas, Bukan Target Ukuran

Ada satu asumsi yang saya bawa terlalu lama: kalau format barunya lebih efisien, hasilnya pasti lebih kecil. VP9 memang lebih efisien dari H.264. Tapi efisiensi codec tidak menjanjikan apa-apa soal ukuran file kalau kamu tidak memberi tahu encoder-nya seberapa boros dia boleh bekerja.

Saya kena batunya waktu merapikan pipeline media di situs portofolio saya sendiri. Video demo proyek masih tersimpan sebagai mp4, dan saya mau memindahkannya ke webm VP9 supaya halaman proyek lebih ringan. Perintahnya sederhana, tinggal jalan sekali, selesai. Begitu pikir saya.

Gejalanya: file webm keluar lebih gemuk dari mp4 sumbernya

Saya pakai angka yang paling sering muncul kalau kamu mencari rekomendasi VP9 untuk web:

ffmpeg -y -i in.mp4 -an \
  -c:v libvpx-vp9 -crf 33 -b:v 0 \
  -row-mt 1 -cpu-used 4 -pix_fmt yuv420p \
  out.webm

Encode-nya sukses. Tidak ada warning yang mencurigakan. Tapi begitu saya bandingkan ukuran input dan output, sebagian webm justru lebih besar dari mp4 yang jadi sumbernya. Bukan selisih tipis yang bisa diabaikan, tapi cukup besar sampai seluruh alasan saya melakukan konversi ini jadi hilang.

Reaksi pertama saya salah arah. Saya sempat curiga flag -b:v 0 yang bikin bitrate lepas kendali, lalu curiga -cpu-used 4 yang mengorbankan kompresi demi kecepatan. Dua-duanya bukan penyebabnya.

Akar masalahnya: CRF menjawab pertanyaan yang salah

-crf di libvpx-vp9 adalah target kualitas konstan, bukan target ukuran. Dipasangkan dengan -b:v 0, artinya kamu benar-benar mematikan batas bitrate dan berkata ke encoder: kejar kualitas segini, pakai bit sebanyak yang kamu butuhkan.

Di situlah masalahnya muncul. Video sumber saya sudah terkompresi habis dari langkah sebelumnya. Detail halusnya sudah lama hilang, dan yang tersisa sebagian adalah artefak kompresi: blok, banding, dan noise buatan yang bentuknya justru sulit ditebak encoder.

VP9 tidak tahu mana yang detail asli dan mana yang bekas luka kompresi. Baginya semua itu isi frame yang harus direproduksi seakurat mungkin sampai target crf 33 tercapai. Jadi encoder-nya rajin, dan hasilnya dia menghabiskan lebih banyak bit daripada yang dipakai sumbernya sendiri.

Jadi crf 33 itu bukan angka yang salah. Dia cuma kelewat murah hati untuk sumber yang kualitasnya sudah tinggal segitu. Kesalahan saya adalah memperlakukan CRF sebagai satu konstanta global, padahal dia harus disetel relatif terhadap kualitas sumbernya.

Perbaikannya: dua angka, bukan satu

Begitu cara pandangnya benar, solusinya jadi membosankan. Saya pisahkan video jadi dua kelompok.

Untuk klip baru yang berkualitas tinggi dan belum pernah dikompresi ulang, crf 33 tetap dipakai:

ffmpeg -y -i in.mp4 -an \
  -c:v libvpx-vp9 -crf 33 -b:v 0 \
  -row-mt 1 -cpu-used 4 -pix_fmt yuv420p \
  out.webm

Untuk video lama yang sudah terkompresi, angkanya saya naikkan ke 38:

ffmpeg -y -i in.mp4 -an \
  -c:v libvpx-vp9 -crf 38 -b:v 0 \
  -row-mt 1 -cpu-used 4 -pix_fmt yuv420p \
  out.webm

Hasilnya langsung masuk akal. Set video lama turun dari 107 MB ke 76 MB, dan setiap file akhirnya lebih kecil dari sumbernya. Klip baru yang tadinya 17 sampai 20 MB per file jatuh ke sekitar 2 MB di crf 33, karena di situ memang ada banyak kualitas berlebih yang layak dibuang.

Perhatikan bahwa yang berubah cuma satu angka. Sisanya tetap:

Thumbnail-nya ikut sekalian

Karena sedang membereskan media, thumbnail statisnya saya konversi ke WebP dengan batas lebar yang masuk akal:

ffmpeg -i in.png -vf "scale='min(1600,iw)':-2" -c:v libwebp -q:v 82 out.webp

min(1600,iw) mencegah gambar kecil di-upscale, dan -2 menjaga tinggi hasilnya tetap genap supaya encoder tidak rewel.

Cek cepat sebelum commit

Pelajaran pahitnya: saya tidak pernah membandingkan ukuran input dan output sampai semuanya selesai dikonversi. Sekarang perbandingan itu jadi bagian dari prosesnya, bukan kejutan di akhir:

for f in *.mp4; do
  printf "%s  %s -> %s\n" "$f" \
    "$(du -h "$f" | cut -f1)" \
    "$(du -h "${f%.mp4}.webm" | cut -f1)"
done

Kalau ada baris yang panahnya menunjuk ke angka lebih besar, naikkan CRF untuk file itu dan encode ulang. Tidak perlu sistem yang canggih, cukup satu tabel yang jujur.

Yang saya bawa pulang