00 Neden donanım codec — CPU vs VPU
Gömülü sistemlerde yazılım encode, CPU'yu tamamen meşgul eder; ısıl kısıtlama (thermal throttling) devreye girerek çalışma süresini kısaltır. VPU, sabit fonksiyon donanımı olduğundan aynı işi çok daha az güçle yapar.
Karşılaştırma tablosu — i.MX8MP örneği
VPU mimarisi
┌────────────────────────────────────────────────────┐
│ SoC (örn: i.MX8MP) │
│ │
│ ┌──────────┐ DMA-BUF ┌────────────────────┐ │
│ │ ISP / │ ──────────→ │ VPU (Video │ │
│ │ V4L2 │ │ Processing Unit) │ │
│ │ kamera │ │ H.264/H.265 HW │ │
│ └──────────┘ └────────┬───────────┘ │
│ │ encoded data │
│ ┌──────────┐ DDR ┌────────▼───────────┐ │
│ │ CPU │ ◄──────────── │ Output buffer │ │
│ │ (ARM) │ │ (filesink/net) │ │
│ └──────────┘ └────────────────────┘ │
└────────────────────────────────────────────────────┘
Zero-copy path: V4L2 DMA-BUF → VPU DMA-BUF input
Kamera frame CPU'ya hiç taşınmaz — doğrudan VPU'ya
Donanım encoder kalite/hız parametreleri yazılım encoder'a göre sınırlıdır. x264enc'in tune/preset seçeneklerinin tamamı VPU'da mevcut değildir. Kalite kritikse yazılım encode tercih edin; güç/ısı kritikse donanım zorunludur.
01 i.MX VPU — imxvpuenc ve mxc_vpu driver
NXP i.MX serisi SoC'larda (i.MX6, i.MX8, i.MX8MP) entegre VPU bulunur. Kernel driver mxc_vpu veya imx-vpu, GStreamer plugin'i gstreamer-imx üzerinden erişilir.
Kernel ve driver durumu
# VPU cihaz düğümlerini kontrol et
ls -la /dev/mxc_vpu* /dev/video* 2>/dev/null
# /dev/mxc_vpu ← i.MX6 VPU (eski driver)
# /dev/video10 ← i.MX8 VPU encode (V4L2 M2M)
# /dev/video12 ← i.MX8 VPU decode (V4L2 M2M)
# Kernel modülünü kontrol et
lsmod | grep vpu
# imx_vpu_v4l2_dec ...
# imx_vpu_v4l2_enc ...
# V4L2 M2M encoder desteklediği formatları listele
v4l2-ctl -d /dev/video10 --list-formats
# Index: 0 (Output)
# Pixel Format: 'NV12' (Y/UV 4:2:0)
# Index: 0 (Capture)
# Pixel Format: 'H264' (H.264)
gstreamer-imx pipeline
# gstreamer-imx plugin'leri kur (Yocto: meta-imx)
# IMAGE_INSTALL += " gstreamer1.0-plugins-imx"
# imxvpuenc_h264 ile hardware encode
gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! \
imxvpuenc_h264 \
bitrate=4000 \
intra-refresh=0 \
idr-interval=30 ! \
h264parse ! \
filesink location=imx_hw.mp4
# v4l2h264enc (standard V4L2 M2M — yeni kernel)
gst-launch-1.0 \
v4l2src device=/dev/video0 io-mode=4 ! \
video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! \
v4l2h264enc \
capture-io-mode=4 \
extra-controls="controls,h264_profile=4,h264_level=41" ! \
video/x-h264,level=\(string\)4 ! \
h264parse ! \
filesink location=v4l2hw.h264
i.MX G2D hardware scaler
# G2D (2D GPU) ile zero-copy format dönüşüm ve scale
# CPU yerine 2D GPU kullanır
gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
video/x-raw,format=YUYV,width=1920,height=1080 ! \
imxvideoconvert_g2d ! \
video/x-raw,format=NV12,width=1280,height=720 ! \
v4l2h264enc ! \
filesink location=g2d_scaled.h264
02 Rockchip MPP — mppvideodec ve rkmpp
Rockchip RK3399, RK3568, RK3588 SoC'ları güçlü Media Process Platform (MPP) VPU'ya sahiptir. rkmpp GStreamer plugin'i ve rockchip-mpp kütüphanesi ile H.264/H.265/VP9 donanım encode/decode desteklenir.
MPP kurulum ve yapı
# Debian/Ubuntu (Armbian/Ubuntu 22.04 for RK3588)
sudo apt install librockchip-mpp1 librockchip-mpp-dev
# gstreamer-rockchip plugin kur
# Genellikle BSP imajında gelir veya kaynak koddan derlenir
# https://github.com/JeffyCN/rockchip_mirrors/gstreamer-rockchip
# VPU cihaz kontrolü
ls -la /dev/mpp_service /dev/rga
# /dev/mpp_service ← Rockchip MPP
# /dev/rga ← Rockchip RGA (2D accelerator)
# MPP codec bilgisi
mpi_enc_test -w 1920 -h 1080 -t 7 -f 0 -o /tmp/test.h264
# MPP INFO: mpp_enc encode success, frame cnt 100
rkmpp GStreamer pipeline
# H.264 hardware encode (RK3568/RK3588)
gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! \
mpph264enc \
bps=4000000 \
bps-target=4000000 \
rc-mode=vbr \
profile=high \
gop=30 ! \
h264parse ! \
filesink location=rk_hw.h264
# H.265 hardware encode (RK3588 — 8K capable)
gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
video/x-raw,format=NV12,width=3840,height=2160,framerate=30/1 ! \
mpph265enc bps=8000000 rc-mode=vbr ! \
h265parse ! \
filesink location=rk_4k_hevc.h265
# Hardware decode
gst-launch-1.0 \
filesrc location=video.h264 ! \
h264parse ! \
mppvideodec ! \
videoconvert ! autovideosink
# RGA ile scale + format dönüşüm
gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
video/x-raw,format=NV12,width=3840,height=2160 ! \
rgaconvert ! \
video/x-raw,format=NV12,width=1920,height=1080 ! \
mpph264enc bps=4000000 ! filesink location=4k_to_1080p.h264
03 Jetson NVENC/NVDEC — nvv4l2 pipeline
NVIDIA Jetson serisinde (Nano, NX, AGX Orin) NVENC (encoder) ve NVDEC (decoder) donanımsal video işleme birimi bulunur. DeepStream SDK veya doğrudan nvv4l2 GStreamer plugin'i ile kullanılır.
Jetson ortam kurulumu
# JetPack kurulu Jetson'da nvv4l2 plugin genellikle hazırdır
gst-inspect-1.0 nvv4l2h264enc
# Plugin Details:
# Name: nvv4l2h264enc
# Description: NVENC Video H264 Encoder
# Version: 1.16.5
# CUDA ve codec bilgisi
jetson_clocks --show
nvpmodel -q
# MAXN veya farklı güç modu seç
# Tüm V4L2 M2M cihazları listele
v4l2-ctl --list-devices
Jetson hardware encode pipeline
# 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 \
bufapi-version=true ! \
h264parse ! \
mp4mux ! filesink location=jetson.mp4
# Jetson H.265 4K encode
gst-launch-1.0 \
v4l2src device=/dev/video0 ! \
video/x-raw,format=NV12,width=3840,height=2160,framerate=30/1 ! \
nvv4l2h265enc bitrate=8000000 iframeinterval=30 ! \
h265parse ! filesink location=jetson_4k.h265
# Jetson hardware decode + display
gst-launch-1.0 \
filesrc location=video.h264 ! \
h264parse ! \
nvv4l2decoder ! \
nvvideoconvert ! \
nv3dsink
# CUDA tabanlı ek işlem için nvdsvideotemplate
04 VA-API — Intel ve AMD GPU encode
VA-API (Video Acceleration API), Linux'ta GPU video işleme için standart arayüzdür. Intel iHD/i965, AMD Mesa RadeonSI driver'ları ile çalışır. Masaüstü ve embedded x86 platformlarda yaygındır.
VA-API durumu kontrol
# VA-API kurulum
sudo apt install vainfo libva-dev gstreamer1.0-vaapi
# Desteklenen profil ve işlemleri listele
vainfo
# vainfo: VA-API version: 1.16 (libva 2.16.0)
# Driver version: Intel iHD driver for Intel(R) Gen Graphics - 22.5
# Supported profile and entrypoints
# ...
# VAProfileH264Main : VAEntrypointEncSliceLP
# VAProfileH264High : VAEntrypointEncSliceLP
# VAProfileH264High : VAEntrypointVLD (decode)
# VAProfileHEVCMain : VAEntrypointEncSliceLP
# ...
# GPU cihaz kontrolü
ls -la /dev/dri/
# renderD128 ← GPU render node (VA-API buna erişir)
gstreamer-vaapi encode pipeline
# 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 \
tune=high-compression \
rate-control=cbr ! \
h264parse ! \
mp4mux ! filesink location=vaapi_h264.mp4
# VAAPI H.265 encode
gst-launch-1.0 \
videotestsrc num-buffers=300 ! \
vaapih265enc bitrate=2000 keyframe-period=30 ! \
h265parse ! \
mp4mux ! filesink location=vaapi_h265.mp4
# VAAPI decode
gst-launch-1.0 \
filesrc location=video.h264 ! \
h264parse ! \
vaapih264dec ! \
vaapipostproc ! \
autovideosink
# VAAPI postprocessing (scale + color)
gst-launch-1.0 \
filesrc location=video.h264 ! h264parse ! vaapih264dec ! \
vaapipostproc \
width=1280 height=720 \
format=nv12 \
deinterlace-mode=bob ! \
vaapih264enc bitrate=2000 ! filesink location=scaled.h264
VAAPI /dev/dri/renderD128 cihazına erişim gerektirir. Kullanıcının render grubunda olması veya uygun yetkiye sahip olması gerekir: sudo usermod -aG render $USER
05 DMA-BUF zero-copy pipeline
DMA-BUF (Direct Memory Access Buffer), çeşitli donanım bileşenlerinin (kamera, GPU, codec) aynı fiziksel bellek bloğuna CPU kopyası olmadan erişmesini sağlar. Gömülü sistemlerde bant genişliği ve gecikme kritik avantaj sağlar.
DMA-BUF mekanizması
Geleneksel pipeline (KOPYALI):
Kamera → [DMA → CPU bellek] → [CPU kopyası → encode giriş] → VPU encode
Bellek kopyası: 2x (1080p NV12 = 3MB × 30fps = 90MB/s kopyalama!)
DMA-BUF zero-copy pipeline:
Kamera → [DMA-BUF fd] → VPU encode (aynı fiziksel bellek)
Bellek kopyası: 0
Faydalar:
- DDR bant genişliği tasarrufu
- CPU meşguliyeti azalma
- Gecikme düşüşü (1-5ms)
- Güç tüketimi azalma
V4L2 → DMA-BUF → encode
# io-mode=4 (DMABUF) ile kamera → encoder zero-copy
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-import \
extra-controls="controls,h264_profile=4" ! \
h264parse ! filesink location=zerocopy.h264
# io-mode değerleri
# dmabuf = v4l2src output DMA-BUF olarak export eder
# dmabuf-import = encoder input DMA-BUF import eder
DMA-BUF C API ile manuel yönetim
#include <gst/gst.h>
#include <gst/allocators/gstdmabuf.h>
#include <gst/video/gstvideometa.h>
/* DMA-BUF buffer'ı kontrol etmek için probe */
static GstPadProbeReturn dmabuf_probe(GstPad *pad, GstPadProbeInfo *info,
gpointer data)
{
GstBuffer *buf = GST_PAD_PROBE_INFO_BUFFER(info);
GstMemory *mem = gst_buffer_peek_memory(buf, 0);
if (gst_is_dmabuf_memory(mem)) {
gint fd = gst_dmabuf_memory_get_fd(mem);
gsize size = gst_memory_get_sizes(mem, NULL, NULL);
g_print("DMA-BUF: fd=%d, size=%zu, pts=%" GST_TIME_FORMAT "\n",
fd, size, GST_TIME_ARGS(GST_BUFFER_PTS(buf)));
} else {
g_print("Uyarı: Buffer DMA-BUF değil — kopya gerekecek!\n");
}
return GST_PAD_PROBE_OK;
}
int main(int argc, char *argv[])
{
GstElement *pipeline, *v4l2src, *enc, *sink;
GstPad *pad;
gst_init(&argc, &argv);
pipeline = gst_pipeline_new("zerocopy");
v4l2src = gst_element_factory_make("v4l2src", "src");
enc = gst_element_factory_make("v4l2h264enc", "enc");
sink = gst_element_factory_make("filesink", "sink");
g_object_set(v4l2src, "device", "/dev/video0",
"io-mode", 4 /* DMABUF */, NULL);
g_object_set(sink, "location", "out.h264", NULL);
gst_bin_add_many(GST_BIN(pipeline), v4l2src, enc, sink, NULL);
/* Caps filter: NV12 formatı encoder için */
GstCaps *caps = gst_caps_from_string(
"video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1");
gst_element_link_filtered(v4l2src, enc, caps);
gst_caps_unref(caps);
gst_element_link(enc, sink);
/* v4l2src src pad'ine probe ekle — DMA-BUF kontrolü */
pad = gst_element_get_static_pad(v4l2src, "src");
gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, dmabuf_probe, NULL, NULL);
gst_object_unref(pad);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
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;
}
06 GstVideoInfo ve format negotiation
Hardware encoder/decoder'lar belirli piksel formatlarını destekler. GstVideoInfo API ile format detaylarını sorgulamak ve caps negotiation'ı doğru yönlendirmek gerekir.
Donanım formatları
GstVideoInfo C API
#include <gst/video/video.h>
void print_video_info(GstCaps *caps)
{
GstVideoInfo info;
if (!gst_video_info_from_caps(&info, caps)) {
g_print("Caps'ten video info alınamadı.\n");
return;
}
g_print("Format : %s\n", gst_video_format_to_string(info.finfo->format));
g_print("Çözünürlük: %dx%d\n", info.width, info.height);
g_print("FPS : %d/%d\n", info.fps_n, info.fps_d);
g_print("Stride[0] : %d byte\n", info.stride[0]);
g_print("Offset[0] : %zu\n", info.offset[0]);
g_print("Toplam : %zu byte/frame\n", info.size);
/* NV12 için Y ve UV plane ayrıntıları */
if (info.finfo->format == GST_VIDEO_FORMAT_NV12) {
g_print("Y plane stride : %d\n", info.stride[0]);
g_print("UV plane stride : %d\n", info.stride[1]);
g_print("UV plane offset : %zu\n", info.offset[1]);
}
}
/* Caps negotiation için yardımcı */
GstCaps* make_hw_caps(gint w, gint h, gint fps_n, gint fps_d)
{
return gst_caps_new_simple("video/x-raw",
"format", G_TYPE_STRING, "NV12",
"width", G_TYPE_INT, w,
"height", G_TYPE_INT, h,
"framerate", GST_TYPE_FRACTION, fps_n, fps_d,
NULL);
}
07 Benchmark metodolojisi
Donanım ve yazılım encode'u karşılaştırmak için tekrarlanabilir ölçüm yöntemi gerekir. CPU kullanımı, termal performans, gerçek bitrate ve kalite metrikleri kayıt altına alınmalıdır.
CPU kullanımı ölçümü
# Yöntem 1: top ile anlık izleme
gst-launch-1.0 -e \
videotestsrc num-buffers=600 ! \
video/x-raw,format=I420,width=1920,height=1080,framerate=30/1 ! \
x264enc speed-preset=ultrafast ! filesink location=/dev/null &
PID=$!
top -b -n 20 -p $PID | grep gst-launch
wait $PID
# Yöntem 2: /usr/bin/time ile toplam CPU
/usr/bin/time -v gst-launch-1.0 \
videotestsrc num-buffers=300 ! \
videoconvert ! x264enc ! filesink location=/dev/null 2>&1 | \
grep "Percent of CPU"
# Yöntem 3: pidstat (sysstat)
pidstat -u 1 -G gst-launch &
PIDSTAT=$!
gst-launch-1.0 videotestsrc num-buffers=300 ! \
videoconvert ! x264enc speed-preset=ultrafast ! filesink location=/dev/null
kill $PIDSTAT
Termal izleme
# Tüm thermal zone sıcaklıkları
watch -n 0.5 'for f in /sys/class/thermal/thermal_zone*/temp; do
zone=$(basename $(dirname $f))
type=$(cat $(dirname $f)/type)
temp=$(cat $f)
printf "%-20s: %d.%d°C\n" "$type" "$((temp/1000))" "$((temp%1000/100))"
done'
# Frekans kısıtlaması izle (i.MX8)
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
# Encode sırasında sıcaklık logu al
(while true; do
date +%s
cat /sys/class/thermal/thermal_zone0/temp
sleep 1
done) > /tmp/temp_log.txt &
TEMP_PID=$!
gst-launch-1.0 -e videotestsrc num-buffers=1800 ! \
videoconvert ! x264enc ! filesink location=/dev/null
kill $TEMP_PID
PSNR/SSIM kalite ölçümü
# Orijinal ve encode edilmiş video PSNR karşılaştırması
ffmpeg -i original.yuv -i encoded.h264 \
-lavfi psnr=psnr.log -f null /dev/null 2>&1 | grep PSNR
# GStreamer ile PSNR (ssim element)
gst-launch-1.0 \
filesrc location=original.y4m ! y4mdec ! tee name=t \
t. ! queue ! identity name=ref ! fakesink \
t. ! queue ! x264enc bitrate=2000 ! avdec_h264 ! \
videomixer name=mix ! \
fakesink \
ref. ! mix.
08 Pratik: i.MX8MP üzerinde 1080p@30 H.264 encode CPU karşılaştırma
NXP i.MX8M Plus üzerinde yazılım (x264enc) ve donanım (v4l2h264enc VPU) encode'u karşılaştıran kapsamlı benchmark senaryosu.
Test ortamı
Benchmark script'i
#!/bin/bash
# i.MX8MP encode benchmark
LOG=/tmp/bench_result.txt
echo "i.MX8MP Encode Benchmark $(date)" > $LOG
run_bench() {
local name="$1"
local pipeline="$2"
local frames=1800
echo "--- $name ---" | tee -a $LOG
# CPU sıcaklığı başlangıç
TEMP_START=$(cat /sys/class/thermal/thermal_zone0/temp)
# Encode + zaman + CPU ölçümü
START_TIME=$(date +%s%N)
/usr/bin/time -f "CPU: %P, MaxRSS: %M kB" \
gst-launch-1.0 -q \
videotestsrc num-buffers=$frames is-live=false ! \
"video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1" ! \
$pipeline filesink location=/dev/null 2>> $LOG
END_TIME=$(date +%s%N)
# Süre hesapla
DURATION=$(( (END_TIME - START_TIME) / 1000000 ))
FPS=$(echo "scale=1; $frames * 1000 / $DURATION" | bc)
TEMP_END=$(cat /sys/class/thermal/thermal_zone0/temp)
echo " Süre : ${DURATION}ms" | tee -a $LOG
echo " FPS : ${FPS}" | tee -a $LOG
echo " Temp : $((TEMP_START/1000))°C → $((TEMP_END/1000))°C" | tee -a $LOG
echo "" | tee -a $LOG
}
# Test 1: Yazılım encode (x264enc ultrafast)
run_bench "Software x264enc (ultrafast)" \
"x264enc speed-preset=ultrafast tune=zerolatency bitrate=4000 ! h264parse !"
# Test 2: Yazılım encode (x264enc fast)
run_bench "Software x264enc (fast)" \
"x264enc speed-preset=fast bitrate=4000 ! h264parse !"
# Test 3: Donanım encode (v4l2h264enc)
run_bench "Hardware v4l2h264enc" \
"v4l2h264enc extra-controls=controls,h264_profile=4 ! h264parse !"
echo "Benchmark tamamlandı. Sonuçlar: $LOG"
Beklenen sonuçlar
i.MX8MP VPU, 1080p@30 encode'u CPU'nun sadece %5'ini kullanarak gerçek zamanlı gerçekleştirir. Yazılım encode ise sınırı zorlar ve termal kısıtlamayla gerçek zamanlılık kaybedilir. Üretim sistemlerinde donanım encoder zorunlu, yazılım encoder sadece yedek veya kalite test amaçlı kullanılmalıdır.