In this page, I'll introduce how to get UVC camera data via v4l2. Here is my environment:
- Dev Board: QiHua-RV1126
- Camera: Rmoncam A2 1080P
- System: Linux 4.19.193 #12 SMP PREEMPT Tue Nov 15 11:21:50 CST 2022 armv7l GNU/Linux
§1 Check Camera Information
We'll use v4l2-ctl
command to check camera information, here is some commands we need:
v4l2-ctl --list-device
, list all video device.v4l2-ctl --all -d /dev/video45
, list all information of video45.v4l2-ctl --list-formats -d /dev/video45
, list output formats of video45.v4l2-ctl --list-formats-ext -d /dev/video45
, list detailed information which include image size and fps.
Here is my uvc camera's info:
[root@DCIR:/]# v4l2-ctl --all -d /dev/video45
Driver Info:
Driver name : uvcvideo
Card type : Rmoncam A2 1080P: RMONCAM A2 10
Bus info : usb-ffe00000.usb-1.3
Driver version : 4.19.193
Capabilities : 0x84a00001
Video Capture
Metadata Capture
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x04200001
Video Capture
Streaming
Extended Pix Format
Media Driver Info:
Driver name : uvcvideo
Model : Rmoncam A2 1080P: RMONCAM A2 10
Serial : 200901010001
Bus info : usb-ffe00000.usb-1.3
Media version : 4.19.193
Hardware revision: 0x00000101 (257)
Driver version : 4.19.193
Interface Info:
ID : 0x03000002
Type : V4L Video
Entity Info:
ID : 0x00000001 (1)
Name : Rmoncam A2 1080P: RMONCAM A2 10
Function : V4L2 I/O
Flags : default
Pad 0x01000004 : 0: Sink
Link 0x0200000d: from remote pad 0x1000007 of entity 'Extension 4': Data, Enabled, Immutable
Priority: 2
Video input : 0 (Camera 1: ok)
Format Video Capture:
Width/Height : 1920/1080
Pixel Format : 'MJPG' (Motion-JPEG)
Field : None
Bytes per Line : 0
Size Image : 4147200
Colorspace : sRGB
Transfer Function : Default (maps to sRGB)
YCbCr/HSV Encoding: Default (maps to ITU-R 601)
Quantization : Default (maps to Full Range)
Flags :
Crop Capability Video Capture:
Bounds : Left 0, Top 0, Width 1920, Height 1080
Default : Left 0, Top 0, Width 1920, Height 1080
Pixel Aspect: 1/1
Selection: crop_default, Left 0, Top 0, Width 1920, Height 1080, Flags:
Selection: crop_bounds, Left 0, Top 0, Width 1920, Height 1080, Flags:
Streaming Parameters Video Capture:
Capabilities : timeperframe
Frames per second: 30.000 (30/1)
Read buffers : 0
brightness 0x00980900 (int) : min=-64 max=64 step=1 default=0 value=0
contrast 0x00980901 (int) : min=0 max=100 step=1 default=32 value=32
saturation 0x00980902 (int) : min=0 max=100 step=1 default=64 value=64
hue 0x00980903 (int) : min=-180 max=180 step=1 default=0 value=0
white_balance_temperature_auto 0x0098090c (bool) : default=1 value=1
gamma 0x00980910 (int) : min=100 max=500 step=1 default=300 value=300
gain 0x00980913 (int) : min=1 max=128 step=1 default=64 value=64
power_line_frequency 0x00980918 (menu) : min=0 max=2 default=1 value=1
white_balance_temperature 0x0098091a (int) : min=2800 max=6500 step=10 default=4600 value=4600 flags=inactive
sharpness 0x0098091b (int) : min=0 max=100 step=1 default=50 value=50
backlight_compensation 0x0098091c (int) : min=0 max=2 step=1 default=0 value=0
exposure_auto 0x009a0901 (menu) : min=0 max=3 default=3 value=3
exposure_absolute 0x009a0902 (int) : min=50 max=10000 step=1 default=166 value=166 flags=inactive
exposure_auto_priority 0x009a0903 (bool) : default=0 value=1
and let's check it's formats:
[root@DCIR:/]# v4l2-ctl --list-formats -d /dev/video45
ioctl: VIDIOC_ENUM_FMT
Type: Video Capture
[0]: 'MJPG' (Motion-JPEG, compressed)
[1]: 'YUYV' (YUYV 4:2:2)
[root@DCIR:/]# v4l2-ctl --list-formats-ext -d /dev/video45
ioctl: VIDIOC_ENUM_FMT
Type: Video Capture
[0]: 'MJPG' (Motion-JPEG, compressed)
Size: Discrete 1920x1080
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 160x120
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 320x240
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 320x180
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 640x480
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 800x600
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 848x480
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 960x540
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 1280x720
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 1280x1024
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 640x360
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 176x144
Interval: Discrete 0.033s (30.000 fps)
[1]: 'YUYV' (YUYV 4:2:2)
Size: Discrete 1920x1080
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 160x120
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 320x240
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 320x180
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 640x480
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 800x600
Interval: Discrete 0.050s (20.000 fps)
Size: Discrete 848x480
Interval: Discrete 0.050s (20.000 fps)
Size: Discrete 960x540
Interval: Discrete 0.050s (20.000 fps)
Size: Discrete 1280x720
Interval: Discrete 0.100s (10.000 fps)
Size: Discrete 1280x1024
Interval: Discrete 0.100s (10.000 fps)
Size: Discrete 640x360
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 176x144
Interval: Discrete 0.033s (30.000 fps)
§2 Example Code
2.1 extern define
I'd like to get yuv data, so I choose V4L2_PIX_FMT_YUYV
as my pix format. Usually, UVC camera is single-planar capture, so our buffer type is V4L2_BUF_TYPE_VIDEO_CAPTURE
, but some MIPI-CSI, DVP and CIF camera maybe multi-planar device, and it's parsing method is different from single-planar device. The method of getting multi-planar camera data will be introduce in subsequent chapters. At the same time, we specify the query buffer number equals 5.
/* =================== */
/* ===== .h file ===== */
/* =================== */
#define _Video_Device_SP_ "/dev/video45"
#define _V4L2_SP_PIX_FMT_ V4L2_PIX_FMT_YUYV
#define _V4L2_SP_BUF_TYPE_ V4L2_BUF_TYPE_VIDEO_CAPTURE
#define _V4L2_SP_BUF_REQ_COUNT_ 5
extern const int v4l2_sp_capture_width;
extern const int v4l2_sp_capture_height;
// device number for v4l2 single-planar
extern int v4l2_sp_fd;
// global memory for mmap v4l2 query buffer, 5 block
extern uint8_t* v4l2_sp_globalBuffer[_V4L2_SP_BUF_REQ_COUNT_];
// global memory buffer length
extern int v4l2_sp_globalBufferLength;
// to specify which memory is currently available
extern int v4l2_sp_globalBufferIndex;
// to specify globalBuffer length
extern int v4l2_sp_globalBufferLength;
// stop capture or not
extern bool v4l2_sp_captureLoopFlag;
/* =================== */
/* ===== .c file ===== */
/* =================== */
const int v4l2_sp_capture_width = 640;
const int v4l2_sp_capture_height = 480;
int v4l2_sp_fd;
uint8_t* v4l2_sp_globalBuffer[_V4L2_SP_BUF_REQ_COUNT_];
int v4l2_sp_globalBufferLength;
int v4l2_sp_globalBufferIndex;
bool v4l2_sp_captureLoopFlag = true;
2.1 implement
int V4L2_SP_Streaming()
{
/* Step 0 : Pre-Define */
int retval = 0; // V4L2_SP_Streaming return value
int ret = 0; // ioctl check
#if Print_Debug_Info
printf("V4L2_SP_Streaming(): Step 1 Open Device\n");
#endif
/* Step 1 : Open Device */
v4l2_sp_fd = open(_Video_Device_SP_, O_RDWR);
if (v4l2_sp_fd == -1)
{
perror("V4L2_SP_Streaming() open device");
retval = 1;
goto out_return;
}
#if Print_Debug_Info
printf("V4L2_SP_Streaming(): Step 2 Setting Capture Format\n");
#endif
/* Step 2: Setting Capture Format */
struct v4l2_format format;
format.type = _V4L2_SP_BUF_TYPE_;
format.fmt.pix.width = v4l2_sp_capture_width;
format.fmt.pix.height = v4l2_sp_capture_height;
format.fmt.pix.pixelformat = _V4L2_SP_PIX_FMT_;
ret = ioctl(v4l2_sp_fd, VIDIOC_S_FMT, &format); // set FMT
if (ret == -1)
{
perror("V4L2_SP_Streaming() setting format");
retval = 2;
goto error_close;
}
#if Print_Debug_Info
printf("V4L2_SP_Streaming(): Step 3 Checking FMT Setting\n");
#endif
/* Step 3: Checking FMT Setting */
ret = ioctl(v4l2_sp_fd, VIDIOC_G_FMT, &format); // get FMT
if (ret == -1)
{
perror("V4L2_SP_Streaming() checking format");
retval = 3;
goto error_close;
}
if (format.fmt.pix.pixelformat == _V4L2_SP_PIX_FMT_) // set FMT == get FMT ?
{
printf("ioctl VIDIOC_S_FMT sucessful\n");
}
else
{
printf("ioctl VIDIOC_S_FMT failed\n");
retval = 4;
goto error_close;
}
#if Print_Debug_Info
printf("V4L2_SP_Streaming(): Step 4 Request Buffer\n");
#endif
/* Step 4: Request Buffer */
struct v4l2_requestbuffers reqbuf;
reqbuf.count = _V4L2_SP_BUF_REQ_COUNT_;
reqbuf.type = _V4L2_SP_BUF_TYPE_;
reqbuf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(v4l2_sp_fd, VIDIOC_REQBUFS, &reqbuf);
if (-1 == ret)
{
perror("V4L2_SP_Streaming() reqeuset buff");
retval = 1;
goto error_close;
}
#if Print_Debug_Info
printf("V4L2_SP_Streaming(): Step 5 mmap Buffer\n");
#endif
/* Step 5: mmap Buffer */
struct v4l2_buffer buff;
buff.type = _V4L2_SP_BUF_TYPE_;
buff.memory = V4L2_MEMORY_MMAP;
for (int i = 0; i < _V4L2_SP_BUF_REQ_COUNT_; ++i)
{
// query buf
buff.index = i;
ret = ioctl(v4l2_sp_fd, VIDIOC_QUERYBUF, &buff);
if (ret == -1)
{
perror("V4L2_SP_Streaming() query");
retval = 2;
goto error_close;
}
printf("buf[%d]: len = %d offset: %d\n", i, buff.length, buff.m.offset);
// mmap
v4l2_sp_globalBuffer[i] = mmap(NULL, buff.length, PROT_READ, MAP_SHARED, v4l2_sp_fd, buff.m.offset);
v4l2_sp_globalBufferLength = buff.length;
if (MAP_FAILED == v4l2_sp_globalBuffer[i])
{
perror("V4L2_SP_Streaming() mmap");
retval = 3;
goto error_munmap;
}
// queue
ret = ioctl(v4l2_sp_fd, VIDIOC_QBUF, &buff);
if (-1 == ret)
{
perror("V4L2_SP_Streaming() queue");
retval = 4;
goto error_munmap;
}
}
#if Print_Debug_Info
printf("V4L2_SP_Streaming(): Step 6 Start Streaming\n");
#endif
/* Step 6: Start Streaming */
int on = _V4L2_SP_BUF_TYPE_;
ret = ioctl(v4l2_sp_fd, VIDIOC_STREAMON, &on);
if (-1 == ret)
{
perror("V4L2_SP_Streaming() VIDIOC_STREAMON");
retval = 5;
goto error_munmap;
}
#if Print_Debug_Info
printf("V4L2_SP_Streaming(): Step 7 Capture\n");
#endif
/* Step 7: Capture */
while (v4l2_sp_captureLoopFlag)
{
// deque
ret = ioctl(v4l2_sp_fd, VIDIOC_DQBUF, &buff);
if (ret != -1)
{
// updata global var
v4l2_sp_globalBufferIndex = buff.index;
v4l2_sp_globalBufferLength = buff.bytesused;
// queue
ret = ioctl(v4l2_sp_fd, VIDIOC_QBUF, &buff);
}
}
#if Print_Debug_Info
printf("V4L2_SP_Streaming(): Exit\n");
#endif
error_munmap:
for (int i = 0; i < _V4L2_SP_BUF_REQ_COUNT_; ++i)
{
if (v4l2_sp_globalBuffer[i] != NULL)
munmap(v4l2_sp_globalBuffer[i], v4l2_sp_globalBufferLength);
}
error_close:
close(v4l2_sp_fd);
out_return:
return retval;
}