Audio/Video Device에 접근할 수 있도록 하는 일종의 Kernel API
V4L2 Flow

동작 순서
- Application에서 ioctl을 이용하여 카메라 그동에 필요한 명령을 순차적으로 전송
- 명렁들의 순서가 바뀌거나 누락되는 경우 V4L2 드라이버에서 오류 발생 확률 증가

- QBUF, DQBUFC, STREAMON은 일반적으로 반복 수행 되어야 영상을 얻을 수 있다.
- 위 동작을 반복하지 않는다면 1 프레임만 얻을 수 있다.
- 대부분 반복문을 사용해서 지속성을 유지 시킴
QEURYCAP (Query Capability)
- 수행되어야 하는 명령 중 가장 간단한 것 중 하나로 연결된 디바이스 이름
- 수행 가능한 동작 등 장치의 정보를 사용자 영역에 알려주는 역할
- 정보들을 v4l2_capability 구조체에 저장
S_FMT_VID_CAP
- 비디오 디바이스를 실질적으로 동작 시키기 전, 디바이스 몇 부분을 설정하는 명령어
- 이미지 포맷, 해상도 등등
V4L2 기본 개념 정리
- V4L2 애플리케이션 (User-Space)
- /dev/videoX 장치를 open() 해서 접근
- ioctl() 호출을 통해 드라이버와 상호작용
- ex) GStreamer, OpenCV, FFmpeg 등에서 활용
- V4L2 드라이버 (Kernel-Space)
- 카메라 센서 드라이버, ISP 드라이버, MIPI CSI/USB 드라이버가 포함됨.
- V4L2 프레임워크를 사용하여 VIDIOC_* 명령을 처리
- V4L2 장치 (Hardware)
- 카메라 센서, 비디오 캡처 카드, SoC의 ISP(이미지 프로세서) 등 물리적인 하드웨어
User-Space:
- open("/dev/video0")
- ioctl(VIDIOC_QUERYCAP) -> 드라이버에게 장치 정보 요청
- ioctl(VIDIOC_S_FMT) -> 해상도, 포맷 설정
- ioctl(VIDIOC_REQBUFS) -> 버퍼 요청
- ioctl(VIDIOC_QBUF) -> 버퍼를 큐에 넣음
- ioctl(VIDIOC_STREAMON) -> 스트리밍 시작
- ioctl(VIDIOC_DQBUF) -> 캡처된 프레임 가져오기
Kernel-Space (드라이버):
- 위 요청을 받아 처리 후 응답
V4L2 장치 파일 : /dev/videoX
- /dev/video0, /dev/video1 … 같은 형태로 존재
- ls /dev/video* → 연결된 V4L2 장치 확인 가능
V4L2 드라이버 정보 조회 : VIDIOC_QUERYCAP
- ioctl(fd, VIDIOC_QUERYCAP, &cap) 호출 시 드라이버가 지원하는 기능 확인 가능
struct v4l2_capability cap;
ioctl(fd, VIDIOC_QUERYCAP, &cap);
printf("Driver: %s\n", cap.driver);
printf("Device: %s\n", cap.card);
printf("Bus: %s\n", cap.bus_info);
printf("Capabilities: %08x\n", cap.capabilities);
V4L2 포맷 설정 : VIDIOC_S_FMT
- 영상 크기나 픽셀 포맷을 설정
- 포맷은 V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_MJPEG 등 이 있음
struct v4l2_format fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 1920;
fmt.fmt.pix.height = 1080;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
ioctl(fd, VIDIOC_S_FMT, &fmt);
V4L2 버퍼 관리 : Memory buffering
V4L2에서는 프레임을 저장할 버퍼를 미리 할당하고, 장치와 사용자 공간이 이를 공유
버퍼 할당 (VIDIOC_REQBUFS)
- V4L2_MEMORY_MMAP또는 V4L2_MEMORY_DMABUF(DMA) 방식 사용 가능
struct v4l2_requestbuffers req;
memset(&req, 0, sizeof(req));
req.count = 4; // 버퍼 개수
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
ioctl(fd, VIDIOC_REQBUFS, &req);
- V4L2_MEMORY_DMABUF로 하면 CPU 개입 없이 DMA로 직접 전송 가능.
버퍼 매핑 (VIDIOC_QUERYBUF, mmap())
- 드라이버에서 할당된 버퍼를 mmap()을 이용해 사용자 공간으로 매핑
struct v4l2_buffer buf;
buf.index = 0;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ioctl(fd, VIDIOC_QUERYBUF, &buf);
void *buffer_start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
V4L2 CAPTURE FLOW
V4L2에서는 Queue와 Dequeue 방식으로 프레임 처리
VIDIOC_QBUF
- 비디오 버퍼를 장치에 등록
struct v4l2_buffer buf;
buf.index = 0;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ioctl(fd, VIDIOC_QBUF, &buf);
VIDIOC_STREAMON
- 장치에서 캡처된 데이터를 가져옴
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMON, $type)
VIDIOC_DQBUF
- 장치에서 캡처된 데이터를 가져옴
struct v4l2_buffer buf;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ioctl(fd, VIDIOC_DQBUF, &buf);
process_frame(buffer_start); // 프레임 데이터 처리
ioctl(fd, VIDIOC_QBUF, &buf); // 다시 큐에 넣기
- 위와 같은 방식으로 프레임이 큐 → 캡처 → 디큐 → 다시 큐 방식으로 순회
V4L2 기본 플로우
1. open("/dev/video0") // 장치 오픈
2. ioctl(VIDIOC_QUERYCAP) // 장치 정보 확인
3. ioctl(VIDIOC_S_FMT) // 포맷 설정
4. ioctl(VIDIOC_REQBUFS) // 버퍼 요청
5. ioctl(VIDIOC_QUERYBUF) // 버퍼 정보 확인
6. mmap() // 버퍼 매핑
7. ioctl(VIDIOC_QBUF) // 버퍼 큐잉
8. ioctl(VIDIOC_STREAMON) // 스트리밍 시작
9. ioctl(VIDIOC_DQBUF) // 프레임 가져오기
10. ioctl(VIDIOC_QBUF) // 버퍼 다시 큐에 넣기
11. ioctl(VIDIOC_STREAMOFF) // 스트리밍 종료
12. close(fd) // 장치 닫기
'임베디드' 카테고리의 다른 글
ARM assembly (0) | 2025.02.20 |
---|---|
Basic #1 (0) | 2024.08.26 |
SocketCAN (0) | 2024.04.22 |
ALSA (0) | 2023.12.21 |
Automotive SPICE (업데이트 中) (0) | 2023.06.20 |