임베디드 개발 노트

V4L2 (Video 4 Linux 2)

Jooyoung Lee 2025. 2. 26. 18:35
반응형
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)  // 장치 닫기
반응형