FFmpeg bukan hanya perintah terminal. Kalian bisa memanggil library libavcodec dan libavformat langsung dari C, Go, atau Python, atau mengotomasi CLI-nya lewat wrapper seperti ffmpeg-python dan fluent-ffmpeg. Episode ini membahas integrasi programatik dan pola error handling yang benar.

Selama 19 episode kalian mengetik FFmpeg di terminal. Tapi di balik binary ffmpeg terdapat puluhan library — libavcodec, libavformat, libavfilter, libavutil, libswscale, libswresample — yang sama persis dengan yang dipakai VLC, OBS, dan Chrome. Library inilah alasan FFmpeg disebut "Swiss Army knife multimedia".
Episode ini membahas sisi programatik: (1) memanggil library libav* langsung dari kode C dengan API resmi, (2) memakai binding di bahasa lain seperti Go dan Python, dan (3) mengotomasi binary ffmpeg lewat wrapper CLI seperti ffmpeg-python di Python dan fluent-ffmpeg di Node. Kalian akan melihat bahwa memahami arsitektur dari episode 1 ternyata punya nilai praktis yang nyata. Sebelum mulai, cek dulu library apa yang ikut terkompilasi di binary kalian dengan ffmpeg -buildconf.
Satu hal yang sering mengecoh pemula: ffmpeg, ffprobe, dan ffplay hanyalah program kecil yang mengombinasikan library-library di bawahnya. Inilah sebabnya satu binary bisa melakukan begitu banyak hal — ia tidak mengimplementasikan apa pun sendiri, melainkan mengorkestrasi library yang sudah matang.
| Library | Tanggung jawab | Analogi |
|---|---|---|
libavformat | Membaca/menulis container (MP4, MKV, WebM) | Gudang dan rak penyimpanan |
libavcodec | Encode/decode codec (H.264, AV1, AAC) | Mesin pengolahan isi |
libavfilter | Grafik filter audio/video | Jalur perakitan |
libavutil | Utilitas dasar, matematika, memory | Kotak alat umum |
libswscale | Konversi format pixel & penskalaan | Perkakas pengubah ukuran |
libswresample | Konversi sample rate & channel audio | Mixer audio |
Konsekuensi praktisnya: aplikasi kalian tidak perlu memanggil ffmpeg binary bila ingin kecepatan maksimal dan kontrol penuh — cukup tautkan ke libavformat dan libavcodec dan panggil API-nya.
C adalah bahasa asli API ini, dan semua binding lain hanyalah pembungkus di atasnya. Alur dasar hampir selalu sama: buka input, temukan stream, buka decoder, baca packet, decode frame.
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <stdio.h>
int main(int argc, char **argv) {
if (argc < 2) return 1;
AVFormatContext *fmt_ctx = NULL;
if (avformat_open_input(&fmt_ctx, argv[1], NULL, NULL) < 0) {
fprintf(stderr, "tidak bisa membuka input\n");
return 1;
}
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
fprintf(stderr, "tidak bisa membaca stream info\n");
return 1;
}
int stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (stream_index < 0) {
fprintf(stderr, "tidak ada stream video\n");
return 1;
}
av_dump_format(fmt_ctx, 0, argv[1], 0);
printf("stream video ada di index %d\n", stream_index);
avformat_close_input(&fmt_ctx);
return 0;
}Perhatikan polanya — ini yang nanti kalian lihat di semua tutorial libav:
avformat_open_input — FFmpeg mengurus alokasi memory, lalu avformat_find_stream_info membaca daftar stream.avformat_close_input — kebocoran memori di C adalah bug yang parah.Compile contoh di atas dengan pkg-config yang menunjuk ke library FFmpeg yang terpasang:
cc probe.c $(pkg-config --cflags --libs libavformat libavcodec) -o probePola "buka → temukan → baca → tutup" ini muncul lagi di setiap tahap decoding, encoding, filtering, dan muxing. Kuasai pola satu kali, dan kalian bisa membaca API apa pun dari keluarga libav*.
Menulis C murni untuk semua proyek tidak selalu praktis. Komunitas menyediakan binding agar library FFmpeg dipakai dari bahasa yang lebih aman dan produktif.
PyAV adalah binding Python yang cukup matang — ia membungkus libav* dan mempertahankan gaya API FFmpeg. Kalian bekerja dengan objek Container, Stream, dan Frame, bukan string perintah.
import av
container = av.open("input.mp4")
stream = container.streams.video[0]
for frame in container.decode(stream):
print(f"frame {frame.index}: {frame.width}x{frame.height} @ {frame.pts}")
img = frame.to_image()
img.save("thumbnail.png")
breakKenapa PyAV unggul untuk kasus seperti ini? Karena frame langsung menjadi objek Python yang bisa diolah (misal jadi PIL.Image) — tanpa lewat parsing stderr dan IPC seperti saat memanggil binary.
Di Go, proyek seperti goav membungkus binding C-style. Karena Go memanggil C lewat cgo, pola dasarnya identik dengan API C — bedanya AVFormatContext ditangani dengan finalizer dan pointer yang lebih aman:
package main
import (
"fmt"
"os"
goav "github.com/giorgisio/goav/avformat"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("usage: probe <file>")
os.Exit(1)
}
ctx := goav.AvformatAllocContext()
if err := ctx.AvformatOpenInput(os.Args[1], nil, nil); err != nil {
fmt.Println("gagal buka input:", err)
os.Exit(1)
}
defer ctx.AvformatCloseInput()
fmt.Printf("durasi: %d detik\n", ctx.Duration()/1000000)
}Binding Go memakai defer untuk menutup context — di sinilah keamanan memory lebih nyaman dibanding C. Struktur API tetap sama, hanya dibungkus idiom Go.
Tidak semua kebutuhan butuh menautkan library. Jika aplikasi kalian hanya menjalankan transcode tertentu, mengotomasi binary ffmpeg lewat wrapper adalah pendekatan paling sederhana dan paling tahan rilis — karena kalian memakai binary apa pun yang terpasang di server. ffmpeg-python membangun command dengan gaya builder: kalian menyusun graph, lalu mengeksekusinya.
import ffmpeg
input_ = ffmpeg.input("input.mp4")
video = input_.video.filter("scale", 1280, 720)
stream = ffmpeg.output(
video, input_.audio,
"output.mp4",
vcodec="libx264", crf=23, preset="fast",
acodec="aac",
)
stdout, stderr = stream.run(capture_stdout=True, capture_stderr=True)Kode di atas setara dengan perintah transcode yang kalian pelajari sejak episode 4, tapi ditulis sebagai objek yang bisa diuji dan di-parametrize dari logika program.
Di sinilah banyak pengguna wrapper tersandung. Default stream.run() melempar exception ffmpeg.Error ketika proses gagal — dan objek exception itu membawa stderr yang menyimpan alasan sebenarnya:
import ffmpeg
try:
ffmpeg.input("missing.mp4").output("out.mp4").run(
capture_stdout=True, capture_stderr=True
)
except ffmpeg.Error as e:
print("stderr:", e.stderr.decode())Aturan praktis error handling di semua wrapper (ffmpeg-python, fluent-ffmpeg, child_process Node):
stderr, bukan hanya kode keluar. Error FFmpeg nyaris selalu eksplisit: No such file, Invalid data, Permission denied. Log juga perintah yang gagal agar mudah direproduksi.ffprobe dengan output JSON, bukan mengurai teks manusia.ffprobe -v error -print_format json -show_format input.mp4Output JSON adalah kontrak yang stabil untuk diprogram. Parsing stderr teks adalah sumber bug yang tidak perlu — jangan pernah membangun parser di atas output yang dirancang untuk dibaca manusia.
Di ekosistem Node, fluent-ffmpeg adalah wrapper yang paling populer. Ia berjalan di atas binary ffmpeg dan menawarkan API berantai dengan event untuk progress dan error.
const ffmpeg = require("fluent-ffmpeg");
ffmpeg("input.mp4")
.output("output.mkv")
.videoCodec("libsvtav1")
.size("1280x720")
.audioCodec("copy")
.on("start", (cmd) => console.log("perintah:", cmd))
.on("progress", (p) => {
if (p.percent) console.log("proses:", p.percent.toFixed(1) + "%");
})
.on("end", () => console.log("selesai"))
.on("error", (err) => {
console.error("gagal:", err.message);
if (err.stderr) console.error(err.stderr);
})
.run();Pola yang sama terlihat lagi: panggil run() sebagai eksekusi akhir, tangani error lewat event, dan baca stderr untuk diagnosis. Event progress bahkan bisa dipakai untuk membangun progress bar — sesuatu yang jauh lebih sulit bila memanggil binary mentah-mentah.
Important
Kapan memakai library (libav*) dan kapan CLI wrapper? Pakai library untuk kontrol per-frame, kecepatan maksimal, atau ribuan file dalam satu proses. Pakai CLI wrapper jika kebutuhan kalian hanya menjalankan transcode yang sudah ditentukan — lebih sederhana, lebih mudah di-debug, dan mengikuti versi binary apa pun yang terpasang. Keduanya sah; pilih berdasarkan kebutuhan.
Episode 20 membuka gerbang integrasi:
libav* dan binary tipis di atasnya.Kalian kini bisa memprogram FFmpeg — tapi ke mana arah perkembangannya? Di episode 21 kita mundur sejenak untuk meninjau fitur modern dan roadmap: rilis 8.0 "Huffman", 8.1 "Hoare", 9.0 "Lei", dan arah masa depan VVC, AV1, serta pemanfaatan GPU. Sampai jumpa di episode 21.