Gömülü bir vision sisteminde 1080p60 NV12 akışı yaklaşık 190 MB/s, 4K30 ise 373 MB/s ham veri üretir. Bu akışı hat boyunca bir kez bile gereksiz yere kopyalarsanız hem DDR bant genişliğini hem de bir CPU çekirdeğinin ciddi bölümünü memcpy'ye harcarsınız — Jetson gibi unified memory mimarilerinde bu bant genişliği aynı zamanda TensorRT inference'ın kullandığı kaynaktır. Bu yazıda kameradan encoder'a kadar CPU'ya hiç uğramayan bir GStreamer hattının nasıl kurulduğunu, appsink üzerinden NVMM buffer'lara güvenli erişimi ve encode gecikmesinin nasıl ölçüleceğini ele alıyoruz. Örnekler Jetson (JetPack 6) üzerinedir; kavramlar i.MX8M Plus gibi VPU'lu platformlara da büyük ölçüde taşınır.
Bellek alanları: system memory, NVMM ve DMA-BUF
GStreamer'da zero-copy, caps müzakeresinde kazanılır ya da kaybedilir. Jetson'da üç bellek alanıyla çalışırsınız:
video/x-raw— klasik system memory. CPU erişimi ucuzdur ama ISP, VIC ve NVENC bu belleğe doğrudan erişemez; donanıma giden her frame kopyalanır.video/x-raw(memory:NVMM)— NvBufSurface destekli buffer. GstBuffer piksel verisi değil, DMA-BUF fd'sine bağlı küçük bir tanımlayıcı taşır. ISP, VIC, NVENC ve GPU aynı fiziksel sayfaları görür; elementler arasında yalnızca fd dolaşır.- DMA-BUF — çekirdek tarafında buffer paylaşımının ortak dili. NVMM bunun üzerine kuruludur;
v4l2src io-mode=dmabufda aynı mekanizmayı kullanır.
Domain geçişlerini nvvidconv (donanımdaki VIC bloğu) yapar ve iki yönde de çalışır. Caps'i açık yazmazsanız müzakere bazen sessizce system memory'de anlaşır; elinizde “çalışan ama yavaş” bir hat kalır.
| Element | Giriş | Çıkış | Not |
|---|---|---|---|
nvarguscamerasrc |
CSI sensör | NVMM | Debayer, 3A ve lens shading ISP donanımında |
v4l2src io-mode=dmabuf |
UVC/V4L2 | DMA-BUF | ISP yok; ilk nvvidconv tek DMA geçişiyle NVMM'e alır |
nvvidconv |
NVMM veya CPU | NVMM veya CPU | Caps'e göre; iki taraf da CPU ise düz memcpy'ye dönüşür |
nvv4l2h264enc |
NVMM | byte-stream | NVENC; CPU caps verirseniz girişte kopya oluşur |
videoconvert |
CPU | CPU | Zero-copy hatta hiç görünmemeli — görünüyorsa müzakere kaybedilmiştir |
Referans hat: kamera → ISP → NVENC
gst-launch-1.0 -v \
nvarguscamerasrc sensor-id=0 ! \
'video/x-raw(memory:NVMM),width=1920,height=1080,framerate=60/1,format=NV12' ! \
nvvidconv ! 'video/x-raw(memory:NVMM),format=NV12' ! \
nvv4l2h264enc maxperf-enable=1 insert-sps-pps=true \
control-rate=1 bitrate=8000000 iframeinterval=60 vbv-size=400000 ! \
h264parse ! rtph264pay config-interval=-1 pt=96 ! \
udpsink host=192.168.1.10 port=5600 sync=false
Buradaki kritik disiplin, her aşamada (memory:NVMM) caps'ini açıkça yazmaktır. -v çıktısındaki negotiated caps'lerde tek bir pad bile NVMM'siz video/x-raw gösteriyorsa o noktada kopya vardır. videoconvert eklemek ya da caps'i eksik yazmak gibi masum görünen değişiklikler müzakereyi CPU'ya düşürür; hat çalışmaya devam eder, sadece yavaşlar. USB kamera kullanıyorsanız ISP devrede olmaz; v4l2src io-mode=dmabuf ! nvvidconv ile frame tek DMA geçişiyle NVMM'e taşınır, hattın gerisi aynıdır.
appsink tuzakları
Analiz veya inference için frame'i uygulamaya çekmenin standart yolu appsink'tir; zero-copy'nin en sık bozulduğu yer de burasıdır:
- Caps vermezseniz kopya alırsınız. appsink'in caps'ini
video/x-raw(memory:NVMM),format=NV12olarak sabitleyin; aksi halde upstream frame'i sizin için system memory'e çevirir ve hat sessizce zero-copy olmaktan çıkar. gst_buffer_mappiksel vermez. NVMM buffer'damap.databirNvBufSurfaceişaretçisidir. Piksele CPU'dan erişeceksenizNvBufSurfaceMapve ardındanNvBufSurfaceSyncForCpugerekir; sync çağrısı cache invalidation yapar, atlarsanız bayat veri okursunuz.- Kuyruk gecikmesi. Canlı hatta
max-buffers=1 drop=true sync=falsekullanın; yoksa tüketici yavaşladığı anda gecikme sessizce yüzlerce milisaniyeye birikir. - Pool küçüktür. NVMM buffer pool tipik olarak birkaç buffer derinliğindedir;
gst_sample_unrefçağrısını geciktirirseniz kamera tarafı stall eder.
static GstFlowReturn on_new_sample(GstAppSink *sink, gpointer data) {
GstSample *sample = gst_app_sink_pull_sample(sink);
GstBuffer *buf = gst_sample_get_buffer(sample);
GstMapInfo map;
gst_buffer_map(buf, &map, GST_MAP_READ);
NvBufSurface *surf = (NvBufSurface *)map.data; /* piksel DEĞİL */
NvBufSurfaceMap(surf, 0, 0, NVBUF_MAP_READ);
NvBufSurfaceSyncForCpu(surf, 0, 0); /* cache invalidate */
/* surf->surfaceList[0].mappedAddr.addr[0] -> Y düzlemi (NV12) */
NvBufSurfaceUnMap(surf, 0, 0);
gst_buffer_unmap(buf, &map);
gst_sample_unref(sample); /* pool'u bekletme */
return GST_FLOW_OK;
}
GPU'da işlem yapacaksanız CPU map'e hiç girmeyin: NvBufSurfaceMapEglImage ile buffer'ı EGLImage olarak kaydedip cuGraphicsEGLRegisterImage üzerinden doğrudan CUDA'ya verin. Frame'in TensorRT'ye CPU'ya hiç inmeden ulaştığı bu kurulum, edge AI pipeline işlerimizdeki varsayılan yoldur.
Encode gecikmesini ölçmek
Gecikmeyi iki ayrı seviyede ölçün; ikisi farklı soruları yanıtlar. Element seviyesinde GStreamer'ın latency tracer'ı yeterlidir:
GST_TRACERS="latency(flags=pipeline+element)" \
GST_DEBUG="GST_TRACER:7" GST_DEBUG_FILE=/tmp/trace.log \
gst-launch-1.0 <hattınız>
grep element-latency /tmp/trace.log | tail -n 50
Tracer, nvv4l2h264enc girişindeki buffer ile çıkışı arasındaki farkı verir — encode gecikmesinin kendisi budur. Düşük gecikme ayarlarının etkisini de burada görürsünüz: küçük vbv-size, CBR (control-rate=1), maxperf-enable=1 ve B-frame'siz profil. Küçük VBV gecikmeyi düşürür ama ani sahne değişimlerinde kaliteyi dalgalandırır; bütçeyi bit hızıyla birlikte ayarlamak gerekir.
Glass-to-glass için tracer yetmez, çünkü sensör pozlaması, ISP ve görüntüleme tarafı hattın dışındadır. Pratik yöntem fizikseldir: kameranın gördüğü bir LED'i GPIO ile yakıp LED'in uzak ekranda belirdiği anı fotodiyotla ya da yüksek hızlı kayıtla yakalamak. Bu düzenekle, burada anlatılan hattın WebRTC ile birleştirilmiş halinde encode gecikmesini 80 ms'nin, glass-to-glass gecikmeyi 200 ms'nin altında ölçtük; düzenek ve sonuçlar WebRTC komuta-kontrol vaka analizinde ayrıntılı anlatılıyor.
Kontrol listesi
-vile her pad'de(memory:NVMM)doğrulandı mı?- Hatta
videoconvertvar mı? Varsa müzakere bir yerde kaybedilmiş demektir. - appsink: NVMM caps +
max-buffers=1 drop=true sync=false+ hızlı unref. - CPU erişiminde
NvBufSurfaceSyncForCpuçağrılıyor mu? - Encode gecikmesi tracer ile, glass-to-glass LED düzeneğiyle ayrı ayrı ölçüldü mü?
Gecikme bütçesi kağıt üzerinde tutuyor ama cihazda tutmuyorsa, hattı caps müzakeresinden encoder ayarlarına kadar ölçüm verisiyle incelediğimiz video pipeline mimari denetimi darboğazı sistematik biçimde bulmak için doğru başlangıç noktasıdır.
