반응형
GStreamer에서 버퍼를 가로채는 3가지 방식 비교
| Pad Probe (gst_pad_add_probe) | identity 같은 통과용 엘리먼트나 특정 Pad에 콜백(Probe)을 등록 | 기존 파이프라인 구조를 해치지 않고 제로카피(In-place) 수정 가능 | 포맷/해상도 변경(Caps 변경) 없이 동일 규격 픽셀 수정에 최적 |
| appsink + appsrc 브릿지 | 디코더 출력을 appsink로 뽑아 처리 후 appsrc로 다시 주입 | GStreamer 스레드와 완전 분리 가능, 비동기 큐/외부 렌더 루프 연동 용이 | 버퍼 풀 재할당 및 약간의 파이프라인 오버헤드 |
| GstVideoFilter 커스텀 플러그인 | GStreamer 기반의 C/C++ 필터 엘리먼트를 직접 작성 | GStreamer 표준 방식, Caps 협상 완벽 지원 | GObject C 보일러플레이트 코드가 많음 |
핵심 동작 및 코드 원리 예시
Gstreamer의 identity 엘리먼트는 버퍼를 수정 없이 그대로 통과시키는 더미 엘리먼트
이 엘리먼트의 sink 패드에 probe를 걸면 버퍼가 통과할 때마다 콜백이 호출됨
// 1. identity 엘리먼트 생성
GstElement* filter = gst_element_factory_make("identity", "pixel-processor");
// 2. sink 패드 가져오기
GstPad* pad = gst_element_get_static_pad(filter, "sink");
// 3. Buffer Probe 등록
gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER,
[](GstPad* pad, GstPadProbeInfo* info, gpointer user_data) -> GstPadProbeReturn {
auto* processor = static_cast<MyProcessor*>(user_data);
// 버퍼 추출
GstBuffer* buffer = GST_PAD_PROBE_INFO_BUFFER(info);
if (!buffer) return GST_PAD_PROBE_OK;
// 중요: In-place 수정을 위해 writable 상태 보장
buffer = gst_buffer_make_writable(buffer);
GST_PAD_PROBE_INFO_DATA(info) = buffer;
// 픽셀 데이터 접근 및 처리 함수 호출
processor->process_frame(pad, buffer);
return GST_PAD_PROBE_OK;
},
this, nullptr
);
gst_object_unref(pad);
-> probe는 return 하지 않으면 버퍼 프레임이 파이프 다음 단계로 진행되지않음
지연과 관련해서 60fps의 경우 각 단계에서 16.6ms를 초과하면 안됨
각 단계라고 하는 것은 스레드로 구분되는 것이므로 queue를 구분점으로하는 각 단계가 16.6ms 넘기면 안됨
따라서 processing 로직의 앞 뒤로 queue를 두어서 최대한 시간 확보 필요
-> queue를 너무 많이 두면 컨텍스트 스위칭 오버헤드와, queue 자체 latency 및 버퍼링으로 전체 지연 발생
텔레오퍼레이션 처럼 실시간 민감도가 높은 작업에서는 지연이 길어지면서 어지러움증 발생 가능
픽셀 메모리에 접근
단순 gst_buffer_map() 사용 시 버퍼에서 stride, padding, plane 등을 전부 직접 계산해야함
따라서 GstVideoInfo + GstVideoFrame 쓰는것이 정석
#include <gst/video/video.h>
void MyProcessor::process_frame(GstPad* pad, GstBuffer* buffer) {
// 1. 현재 Pad의 Caps(해상도, 포맷 등) 정보 가져오기
GstCaps* caps = gst_pad_get_current_caps(pad);
GstVideoInfo vinfo;
if (!gst_video_info_from_caps(&vinfo, caps)) {
if (caps) gst_caps_unref(caps);
return;
}
gst_caps_unref(caps);
// 2. 버퍼를 Video Frame으로 매핑 (Read/Write)
GstVideoFrame frame;
if (!gst_video_frame_map(&frame, &vinfo, buffer, GST_MAP_READWRITE)) {
return;
}
// 3. 픽셀 포인터 획득
// 예: RGBA 포맷인 경우:
uint8_t* pixels = GST_VIDEO_FRAME_PLANE_DATA(&frame, 0); // 픽셀 시작 주소
int stride = GST_VIDEO_FRAME_PLANE_STRIDE(&frame, 0); // 한 줄의 바이트 수 (pitch)
int width = GST_VIDEO_FRAME_WIDTH(&frame);
int height = GST_VIDEO_FRAME_HEIGHT(&frame);
// 4. 커널 실행 (예: CPU 병렬 또는 OpenGL / Custom 처리기 호출)
// kernel->execute(pixels, width, height, stride);
// 5. 언맵 필수
gst_video_frame_unmap(&frame);
}
파이프라인 구성 예시
[Decoder] -> [videoconvert] -> [capsfilter (video/x-raw, format=RGBA)] -> [identity (Probe로 픽셀 조작)] -> [Sink]
videoconvert + capsfilter 조합으로 색상 포맷 고정
참고 레퍼런스
- GStreamer Pad Probes
- 공식 문서: GStreamer Documentation - Pad Probes
- 키워드: gst_pad_add_probe, GST_PAD_PROBE_TYPE_BUFFER, gst_buffer_make_writable
- GStreamer Video Library (gstvideo)
- 헤더: <gst/video/video.h>
- 키워드: GstVideoInfo, gst_video_info_from_caps, GstVideoFrame, gst_video_frame_map
- 주의점: stride != width * bytes_per_pixel일 수 있으므로(메모리 정렬 패딩), 반복문 돌 때 반드시 stride 단위로 오프셋을 계산해야 왜곡이 생기지 않습니다.
- CUDA 대체 병렬 처리 구현체 (향후 커널 작성 시)
- CPU 단순 병렬:
- OpenMP (#pragma omp parallel for)
- C++17 Parallel Algorithms (std::for_each(std::execution::par_unseq, ...))
- GPU (OpenGL):
- PBO (Pixel Buffer Object): CPU 메모리(pixels)를 glTexSubImage2D로 GPU에 업로드할 때 비동기 DMA 전송 지원.
- Compute Shader (OpenGL 4.3+ / GLES 3.1+): CUDA 커널과 거의 동일한 방식으로 픽셀을 병렬 조작 가능.
- CPU 단순 병렬:
반응형
'Engineering' 카테고리의 다른 글
| SYSTEM PERFORMANCE (0) | 2026.10.12 |
|---|---|
| 실험 : RTSP 기반 스트리밍 파이프라인 레이턴시 및 QoE 분석 (0) | 2026.09.25 |
| QoS (Quality of Service) (0) | 2026.09.13 |
| Multimedia Systems (0) | 2026.09.10 |
| latency optimization in interactive multimedia streaming (0) | 2026.09.10 |