Linux Media 01 Get UVC Camera Data via V4L2

发布于 2022-11-25  158 次阅读


  In this page, I'll introduce how to get UVC camera data via v4l2. Here is my environment:

  1. Dev Board: QiHua-RV1126
  2. Camera: Rmoncam A2 1080P
  3. 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:

  1. v4l2-ctl --list-device, list all video device.
  2. v4l2-ctl --all -d /dev/video45, list all information of video45.
  3. v4l2-ctl --list-formats -d /dev/video45, list output formats of video45.
  4. 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;
}