00 Video codec temelleri — H.264, H.265, AV1, VP9
Codec seçimi; bant genişliği, CPU/VPU yükü, gecikme ve uyumluluk arasındaki dengeyi belirler. Gömülü sistemlerde donanım desteği olan codec tercih edilmelidir.
Codec karşılaştırması
Frame türleri
Bitrate modları
Gömülü sistemlerde B-frame'leri kapatmak (b-frames=0), tune=zerolatency kullanmak ve IDR aralığını düşük tutmak (key-int-max=30) gecikmeyi minimize eder.
01 v4l2src — kamera format ve yapılandırma
v4l2src, Video4Linux2 arayüzü üzerinden kameradan ham frame alan GStreamer source element'idir. Format seçimi, sonraki pipeline element'lerini doğrudan etkiler.
Yaygın kamera formatları
v4l2src özellikleri ve caps
# Kamera caps listesi (GStreamer ile)
gst-device-monitor-1.0 Video/Source
# YUYV 1280x720 @ 30fps
gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
video/x-raw,format=YUYV,width=1280,height=720,framerate=30/1 ! \
videoconvert ! autovideosink
# MJPEG 1920x1080 @ 30fps (USB kamera)
gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
image/jpeg,width=1920,height=1080,framerate=30/1 ! \
jpegdec ! videoconvert ! autovideosink
# io-mode: userptr DMA transfer (sıfır kopya için)
gst-launch-1.0 \
v4l2src device=/dev/video0 io-mode=4 ! \
video/x-raw,format=NV12,width=1920,height=1080 ! \
v4l2h264enc ! filesink location=out.h264
V4L2 kontrol ayarları
# Parlaklık, kontrast gibi kamera kontrollerini listele
v4l2-ctl -d /dev/video0 --list-ctrls
# GStreamer içinden v4l2 kontrol yaz
gst-launch-1.0 \
v4l2src device=/dev/video0 extra-controls="c,brightness=128,contrast=64" ! \
videoconvert ! autovideosink
02 Video dönüşüm element'leri
videoconvert, videoscale ve videorate; pipeline içindeki format, çözünürlük ve kare hızı dönüşümlerini gerçekleştiren temel filter element'leridir.
Zincir örneği
# 1080p@60 kamera → 720p@30 → H.264 encode
gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
video/x-raw,format=YUYV,width=1920,height=1080,framerate=60/1 ! \
videoconvert ! \
videoscale ! \
video/x-raw,width=1280,height=720 ! \
videorate ! \
video/x-raw,framerate=30/1 ! \
x264enc bitrate=2000 ! \
mp4mux ! filesink location=out.mp4
# videoscale metodu seç
gst-launch-1.0 videotestsrc ! \
videoscale method=bilinear ! \
video/x-raw,width=640,height=360 ! \
autovideosink
videoconvert → videoscale → videoconvert zinciri gereksiz CPU yükü yaratır. Mümkünse hardware üzerinde tek format + tek ölçekleyici (örn: imxvideoconvert_g2d i.MX8'de) tercih edin. Önce ölçekle, sonra format dönüştür sırası genellikle daha verimlidir.
03 Software encode — x264enc ve x265enc
x264 ve x265, açık kaynaklı referans H.264/H.265 encoder'larıdır. CPU kullanımı yüksektir; ancak donanım encoder'ı olmayan sistemlerde veya kalite öncelikli senaryolarda tercih edilir.
x264enc parametreleri
# Temel kullanım
gst-launch-1.0 \
videotestsrc ! videoconvert ! \
x264enc bitrate=2000 ! \
mp4mux ! filesink location=sw_encode.mp4
# Düşük gecikme (RTSP/streaming için)
gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
video/x-raw,format=YUYV,width=1280,height=720 ! \
videoconvert ! \
x264enc \
tune=zerolatency \
speed-preset=ultrafast \
bitrate=1500 \
key-int-max=30 \
b-adapt=false \
bframes=0 ! \
rtph264pay ! \
udpsink host=192.168.1.100 port=5000
# Yüksek kalite arşiv (CRF modu)
gst-launch-1.0 -e \
v4l2src device=/dev/video0 num-buffers=300 ! \
video/x-raw,format=YUYV,width=1920,height=1080 ! \
videoconvert ! \
x264enc \
pass=qual \
quantizer=21 \
profile=high ! \
mp4mux ! filesink location=hq.mp4
x265enc parametreleri
# HEVC encode — H.264'e göre ~2x daha verimli sıkıştırma
gst-launch-1.0 \
videotestsrc num-buffers=150 ! \
video/x-raw,format=I420,width=1920,height=1080,framerate=30/1 ! \
x265enc \
bitrate=1000 \
speed-preset=fast \
tune=zerolatency ! \
h265parse ! \
mp4mux ! filesink location=hevc.mp4
# Not: x265enc çıkışı için h265parse gerekebilir
04 Hardware encode — v4l2h264enc, VAAPI, Jetson
Donanım encoder'ları; CPU'yu devre dışı bırakarak VPU/GPU üzerinde encode yapar. Güç tüketimi ve ısı açısından gömülü sistemler için kritiktir.
v4l2h264enc (i.MX8 / Raspberry Pi)
# Raspberry Pi 4 — hardware H.264 encode
gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
video/x-raw,format=YUY2,width=1920,height=1080,framerate=30/1 ! \
v4l2convert ! \
video/x-raw,format=NV12 ! \
v4l2h264enc extra-controls="controls,repeat_sequence_header=1" ! \
video/x-h264,level=(string)4 ! \
h264parse ! \
mp4mux ! filesink location=rpi_hw.mp4
# i.MX8MP — hardware encode + DMA-BUF
gst-launch-1.0 \
v4l2src device=/dev/video0 io-mode=dmabuf ! \
video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! \
v4l2h264enc capture-io-mode=dmabuf ! \
filesink location=imx8_hw.h264
# Encoder kontrollerini listele
v4l2-ctl -d /dev/video1 --list-ctrls
# /dev/video1 genellikle encode, /dev/video0 kamera
VA-API (Intel / AMD)
# VA-API bilgilerini kontrol et
vainfo
# vainfo: VA-API version: 1.16
# Driver version: Intel iHD driver ...
# Supported profile and entrypoints:
# VAAPI H.264 encode
gst-launch-1.0 \
videotestsrc num-buffers=300 ! \
video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! \
vaapih264enc bitrate=4000 keyframe-period=30 ! \
mp4mux ! filesink location=vaapi.mp4
# VAAPI H.265 encode
gst-launch-1.0 \
videotestsrc ! \
vaapih265enc bitrate=2000 ! \
h265parse ! \
mp4mux ! filesink location=vaapi_hevc.mp4
Jetson NVENC (nvv4l2h264enc)
# Jetson hardware H.264 encode
gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! \
nvv4l2h264enc \
bitrate=4000000 \
iframeinterval=30 \
preset-level=1 \
insert-sps-pps=true ! \
h264parse ! \
mp4mux ! filesink location=jetson_hw.mp4
# Jetson H.265 encode
gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
video/x-raw,format=NV12,width=3840,height=2160 ! \
nvv4l2h265enc bitrate=8000000 ! \
h265parse ! \
filesink location=jetson_4k.h265
Donanım encoder'larının büyük çoğunluğu sadece NV12 veya I420 formatını kabul eder. Kamera YUYV çıkış veriyorsa videoconvert veya v4l2convert ile dönüşüm zorunludur. Ancak bu dönüşüm CPU'ya yük getirir; mümkünse kamerayı doğrudan NV12 modunda yapılandırın.
05 Decode pipeline — decodebin ve donanım decode
Decode tarafında GStreamer'ın otomatik bağlama özelliği (decodebin/uridecodebin) büyük kolaylık sağlar. Donanım decoder ile CPU decode arasındaki fark çok büyüktür.
decodebin — otomatik decode
# Herhangi bir video dosyasını otomatik decode et ve göster
gst-launch-1.0 \
filesrc location=video.mp4 ! \
decodebin ! \
videoconvert ! \
autovideosink
# uridecodebin — URI destekli (file://, rtsp://, http://)
gst-launch-1.0 \
uridecodebin uri=file:///home/user/video.mp4 ! \
videoconvert ! \
autovideosink
# playbin — tam oynatıcı (audio+video+subtitle)
gst-launch-1.0 \
playbin uri=file:///home/user/video.mp4
Manuel H.264 decode pipeline
# Software decode (avdec_h264 — libav/FFmpeg)
gst-launch-1.0 \
filesrc location=video.h264 ! \
h264parse ! \
avdec_h264 ! \
videoconvert ! \
autovideosink
# V4L2 hardware decode (i.MX8 / RPi)
gst-launch-1.0 \
filesrc location=video.h264 ! \
h264parse ! \
v4l2h264dec ! \
videoconvert ! \
autovideosink
# Jetson hardware decode
gst-launch-1.0 \
filesrc location=video.h264 ! \
h264parse ! \
nvv4l2decoder ! \
nvvideoconvert ! \
nv3dsink
# VAAPI decode (Intel/AMD)
gst-launch-1.0 \
filesrc location=video.h264 ! \
h264parse ! \
vaapih264dec ! \
vaapipostproc ! \
autovideosink
06 Sink seçenekleri — filesink, udpsink, appsink
Pipeline'ın son noktası sink element'idir. Hedef; depolama, ağ, ekran veya uygulama tampon belleği olabilir.
appsink ile buffer yakalama
#include <gst/gst.h>
#include <gst/app/gstappsink.h>
static GstFlowReturn new_sample_cb(GstAppSink *sink, gpointer data)
{
GstSample *sample;
GstBuffer *buffer;
GstMapInfo map;
sample = gst_app_sink_pull_sample(sink);
if (!sample) return GST_FLOW_ERROR;
buffer = gst_sample_get_buffer(sample);
gst_buffer_map(buffer, &map, GST_MAP_READ);
/* map.data → ham piksel verisi, map.size → byte boyutu */
g_print("Frame alındı: %zu byte, ts=%" GST_TIME_FORMAT "\n",
map.size, GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));
/* Burada frame işleme: OpenCV, TFLite vb. */
gst_buffer_unmap(buffer, &map);
gst_sample_unref(sample);
return GST_FLOW_OK;
}
int main(int argc, char *argv[])
{
GstElement *pipeline, *appsink_el;
GstAppSink *appsink;
gst_init(&argc, &argv);
pipeline = gst_parse_launch(
"v4l2src device=/dev/video0 ! "
"video/x-raw,format=RGB,width=640,height=480 ! "
"videoconvert ! "
"video/x-raw,format=RGB ! "
"appsink name=sink emit-signals=true max-buffers=1 drop=true",
NULL);
appsink_el = gst_bin_get_by_name(GST_BIN(pipeline), "sink");
appsink = GST_APP_SINK(appsink_el);
/* Yeni frame sinyalini bağla */
g_signal_connect(appsink, "new-sample", G_CALLBACK(new_sample_cb), NULL);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
/* Ana döngü */
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(loop);
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(pipeline);
return 0;
}
07 Kalite ve performans analizi
Pipeline performansını ölçmek; darboğazları tespit etmek, parametre optimizasyonu yapmak ve ısıl sorunu önceden görmek için kritiktir.
GST_DEBUG ile log analizi
# Seviye: 0=NONE, 1=ERROR, 2=WARN, 3=FIXME, 4=INFO, 5=DEBUG, 6=LOG, 7=TRACE
export GST_DEBUG="*:3" # tüm kategoriler WARNING+
export GST_DEBUG="v4l2src:5,x264enc:4" # seçici
export GST_DEBUG_FILE=/tmp/gst.log # dosyaya yaz
# Renk log (terminal)
export GST_DEBUG_COLOR_MODE=on
# Şema (dot) dosyası oluştur — pipeline görselleştirme
export GST_DEBUG_DUMP_DOT_DIR=/tmp/gst_dots
# Pipeline PLAYING'e geçince .dot dosyası oluşur
# dot -Tpng pipeline.dot -o pipeline.png
Performans ölçüm araçları
# fpsdisplaysink ile gerçek zamanlı FPS izleme
gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
video/x-raw,width=1920,height=1080,framerate=30/1 ! \
videoconvert ! \
fpsdisplaysink video-sink=autovideosink sync=false text-overlay=true
# CPU kullanımını ölç (encode sırasında)
pidstat -u 1 &
gst-launch-1.0 -e \
v4l2src device=/dev/video0 num-buffers=300 ! \
videoconvert ! x264enc ! filesink location=/dev/null
# Pipeline gecikme analizi
GST_DEBUG="GST_PERFORMANCE:5" gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
videoconvert ! \
x264enc tune=zerolatency ! \
udpsink host=127.0.0.1 port=5000 2>&1 | grep latency
Uzun süre 1080p encode sırasında gömülü sistemlerde termal kısıtlama (throttling) devreye girebilir. watch -n1 cat /sys/thermal/thermal_zone0/temp ile sıcaklığı izleyin. Soğutma yeterliyse donanım encoder tercih edin — güç tüketimi yazılım encode'a göre genellikle 5-10x daha düşüktür.
08 Pratik: kamera → H.264 MP4 + eş zamanlı UDP stream
Tek kamera kaynağından hem yüksek kaliteli MP4 dosyaya yazma hem de düşük gecikmeli UDP stream — tee element ile eş zamanlı iki çıkış yönetimi.
Pipeline tasarımı
/dev/video0
YUYV→I420
çoğalt
yüksek kalite
düşük gecikme
gst-launch komutu
# Kamera → tee → (MP4 dosya) + (UDP RTP stream)
gst-launch-1.0 -e \
v4l2src device=/dev/video0 ! \
video/x-raw,format=YUYV,width=1280,height=720,framerate=30/1 ! \
videoconvert ! \
video/x-raw,format=I420 ! \
tee name=t \
t. ! queue max-size-buffers=5 leaky=downstream ! \
x264enc bitrate=3000 speed-preset=fast key-int-max=30 ! \
mp4mux ! filesink location=kayit.mp4 \
t. ! queue max-size-buffers=2 leaky=downstream ! \
x264enc bitrate=1000 speed-preset=ultrafast tune=zerolatency ! \
rtph264pay config-interval=1 pt=96 ! \
udpsink host=192.168.1.100 port=5000
Alıcı tarafı (VLC veya GStreamer)
# VLC ile izle
vlc rtp://0.0.0.0:5000
# GStreamer alıcı pipeline
gst-launch-1.0 \
udpsrc port=5000 ! \
application/x-rtp,encoding-name=H264,payload=96 ! \
rtph264depay ! \
h264parse ! \
avdec_h264 ! \
videoconvert ! \
fpsdisplaysink sync=false
C uygulaması — gst_parse_launch ile
#include <gst/gst.h>
#include <signal.h>
static GMainLoop *loop;
static void sig_handler(int s) { g_main_loop_quit(loop); }
static gboolean bus_cb(GstBus *bus, GstMessage *msg, gpointer data)
{
switch (GST_MESSAGE_TYPE(msg)) {
case GST_MESSAGE_EOS:
g_main_loop_quit((GMainLoop *)data); break;
case GST_MESSAGE_ERROR: {
GError *e; gchar *d;
gst_message_parse_error(msg, &e, &d);
g_printerr("HATA: %s\n", e->message);
g_error_free(e); g_free(d);
g_main_loop_quit((GMainLoop *)data); break;
}
default: break;
}
return TRUE;
}
int main(int argc, char *argv[])
{
const char *host = argc > 1 ? argv[1] : "127.0.0.1";
gchar *desc;
GstElement *pipeline;
GstBus *bus;
gst_init(&argc, &argv);
signal(SIGINT, sig_handler);
desc = g_strdup_printf(
"v4l2src device=/dev/video0 ! "
"video/x-raw,format=YUYV,width=1280,height=720,framerate=30/1 ! "
"videoconvert ! video/x-raw,format=I420 ! "
"tee name=t "
"t. ! queue leaky=2 ! "
" x264enc bitrate=3000 speed-preset=fast key-int-max=30 ! "
" mp4mux ! filesink location=kayit.mp4 "
"t. ! queue leaky=2 max-size-buffers=2 ! "
" x264enc bitrate=1000 speed-preset=ultrafast tune=zerolatency ! "
" rtph264pay config-interval=1 pt=96 ! "
" udpsink host=%s port=5000", host);
pipeline = gst_parse_launch(desc, NULL);
g_free(desc);
loop = g_main_loop_new(NULL, FALSE);
bus = gst_element_get_bus(pipeline);
gst_bus_add_watch(bus, bus_cb, loop);
gst_object_unref(bus);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
g_print("Kayıt ve stream başladı → %s:5000\n", host);
g_main_loop_run(loop);
/* EOS → dosyayı kapat */
gst_element_send_event(pipeline, gst_event_new_eos());
g_usleep(500000); /* 0.5s MP4 kapansın */
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(pipeline);
g_main_loop_unref(loop);
return 0;
}
tee sonrası her dal mutlaka queue içermelidir. Aksi halde iki dal aynı thread'de çalışır, biri diğerini bloklar. leaky=downstream ile dolduğunda en eski buffer atılır — canlı stream senkronunu korur. Dosya yazan dal için leaky=0 (hiç atma) tercih edilmelidir.