Remote video control series - mjpg stream source code analysis - init_v4l2

Entry parameter: struct vdIn * vd.

1. Turn on a usb camera

if((vd->fd = OPEN_VIDEO(vd->videodevice, O_RDWR)) == -1) {
        perror("ERROR opening V4L interface");
        DBG("errno: %d", errno);
        return -1;
    }

#define OPEN_VIDEO(fd, flags) open(fd, flags)

The main function is the open function, where VD - > videodevice = / dev / Video1 indicates the usb camera on my development board.

2. Query driven function

    memset(&vd->cap, 0, sizeof(struct v4l2_capability));
    ret = xioctl(vd->fd, VIDIOC_QUERYCAP, &vd->cap);

    #define IOCTL_VIDEO(fd, req, value) ioctl(fd, req, value)

The key function of query driven function is xioctl, which actually encapsulates the ioctl function. The key of this function is its attribute instruction. To query the function of the driver, you need: VIDIOC_QUERYCAP instruction. After the query, where do you want to put the queried driver functions? This leads to the first important structure: struct v4l2_capability; This structure is also a relatively simple structure, which is reflected in that the member variables of this structure do not contain other structures.

The prototype of the structure is as follows:

struct v4l2_capability
{
	u8 driver[16]; 		// Driver name
	u8 card[32]; 		// Device name
	u8 bus_info[32]; 	// Location of equipment in the system
	u32 version; 		// Drive version number
	u32 capabilities; 	// Operations supported by the device
	u32 reserved[4]; 	// Reserved fields
};

3. Set image format

    memset(&vd->fmt, 0, sizeof(struct v4l2_format));
    vd->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vd->fmt.fmt.pix.width = vd->width;
    vd->fmt.fmt.pix.height = vd->height;
    vd->fmt.fmt.pix.pixelformat = vd->formatIn;
    vd->fmt.fmt.pix.field = V4L2_FIELD_ANY;
    ret = xioctl(vd->fd, VIDIOC_S_FMT, &vd->fmt);

The format to be set is nothing more than the size and coding format of the video. The ioctl function is also used for setting. There are only three axes using this function: ioctl, instruction and structure. The format setting instruction is vidioc_ S_ For FMT, the structure used is struct v4l2_format, which is more complex than the one above. Its prototype is:

struct v4l2_format {  
    enum v4l2_buf_type type;  
    union {  
        struct v4l2_pix_format         pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */  
        struct v4l2_window             win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */  
        struct v4l2_vbi_format         vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */  
        struct v4l2_sliced_vbi_format  sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */  
        __u8   raw_data[200];                   /* user-defined */  
    } fmt;  
};

There are only two member variables in the structure, type and fmt, and the parameter type is the third of the three axes using ioctl function: the most important parameter in the structure. Type is v4l2_buf_type, which was originally scheduled as follows:

enum v4l2_buf_type {
        V4L2_BUF_TYPE_VIDEO_CAPTURE        = 1,
        V4L2_BUF_TYPE_VIDEO_OUTPUT         = 2,
        V4L2_BUF_TYPE_VIDEO_OVERLAY        = 3,
        V4L2_BUF_TYPE_VBI_CAPTURE          = 4,
        V4L2_BUF_TYPE_VBI_OUTPUT           = 5,
        V4L2_BUF_TYPE_SLICED_VBI_CAPTURE   = 6,
        V4L2_BUF_TYPE_SLICED_VBI_OUTPUT    = 7,
        V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
        V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,
        V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE  = 10,
        V4L2_BUF_TYPE_SDR_CAPTURE          = 11,
        V4L2_BUF_TYPE_SDR_OUTPUT           = 12,
        V4L2_BUF_TYPE_META_CAPTURE         = 13,
        /* Deprecated, do not use */
        V4L2_BUF_TYPE_PRIVATE              = 0x80,
};

In the video capture phase, the type will almost certainly be set to: V4L2_BUF_TYPE_VIDEO_CAPTURE. Since type is so important to the third axe, what's the use of it? Here we will introduce a parameter that is extremely important to type, consortium fmt! The content of the consortium is determined according to the selection of type, where type = V4L2_BUF_TYPE_VIDEO_CAPTURE, so the consortium fmt = struct v4l2_pix_format. Here we might as well take a look v4l2_ pix_ What does the structure format describe.

struct v4l2_pix_format {  
    __u32                   width;   // Width, must be a multiple of 16
    __u32                   height;    // High, must be a multiple of 16
    __u32                   pixelformat;   // Video data storage type, such as / / YUV4:2:2 or RGB
    enum v4l2_field         field;  
    __u32                   bytesperline;   /* for padding, zero if unused */  
    __u32                   sizeimage;  
    enum v4l2_colorspace    colorspace;  
    __u32                   priv;           /* private data, depends on pixelformat */  
};

The structure stores the basic format parameters of the image. By configuring the structure, the image parameters of the user space can be written to the device through ioctl.

enum v4l2_field {
        V4L2_FIELD_ANY           = 0, /* driver can choose from none,
                                         top, bottom, interlaced
                                         depending on whatever it thinks
                                         is approximate ... */
        V4L2_FIELD_NONE          = 1, /* this device has no fields ... */
        V4L2_FIELD_TOP           = 2, /* top field only */
        V4L2_FIELD_BOTTOM        = 3, /* bottom field only */
        V4L2_FIELD_INTERLACED    = 4, /* both fields interlaced */
        V4L2_FIELD_SEQ_TB        = 5, /* both fields sequential into one
                                         buffer, top-bottom order */
        V4L2_FIELD_SEQ_BT        = 6, /* same as above + bottom-top order */
        V4L2_FIELD_ALTERNATE     = 7, /* both fields alternating into
                                         separate buffers */
        V4L2_FIELD_INTERLACED_TB = 8, /* both fields interlaced, top field
                                         first and the top field is
                                         transmitted first */
        V4L2_FIELD_INTERLACED_BT = 9, /* both fields interlaced, top field
                                         first and the bottom field is
                                         transmitted first */
};

4. Set the frame rate

    struct v4l2_streamparm *setfps;
    setfps = (struct v4l2_streamparm *) calloc(1, sizeof(struct v4l2_streamparm));
    memset(setfps, 0, sizeof(struct v4l2_streamparm));
    setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    setfps->parm.capture.timeperframe.numerator = 1;
    setfps->parm.capture.timeperframe.denominator = vd->fps;
    ret = xioctl(vd->fd, VIDIOC_S_PARM, setfps);

It also uses three axes: ioctl, instruction and structure. The instruction to set the frame rate is VIDIOC_S_PARM, the structure is:

struct v4l2_streamparm. The prototype of the structure is as follows:
 

/*      Stream type-dependent parameters
 */
struct v4l2_streamparm {
        __u32    type;                  /* enum v4l2_buf_type */
        union {
                struct v4l2_captureparm capture;
                struct v4l2_outputparm  output;
                __u8    raw_data[200];  /* user-defined */
        } parm;
};

For this structure, the key is the type member variable, type = V4L2_BUF_TYPE_VIDEO_CAPTURE. The key to type is the consortium parm. If type is the capture type, so is the union parm, that is, parm = struct v4l2_captureparm. For this structure, its prototype is as follows:

/*
 *      C A P T U R E   P A R A M E T E R S
 */
struct v4l2_captureparm {
        __u32              capability;    /*  Supported modes */
        __u32              capturemode;   /*  Current mode */
        struct v4l2_fract  timeperframe;  /*  Time per frame in seconds */
        __u32              extendedmode;  /*  Driver-specific extensions */
        __u32              readbuffers;   /*  # of buffers for read */
        __u32              reserved[4];
};

V4l2 is used in this structure_ The prototype of frame structure is as follows:

struct v4l2_fract {
        __u32   numerator;
        __u32   denominator;
};

numerator represents the denominator, which is generally set to 1, and denominator represents the denominator, which is generally the frame rate. From the interlocking of the above structures, the key link in setting the frame rate is the structure, which saves the value to be set, so it needs to be initialized at the beginning.

5. Application cache

    memset(&vd->rb, 0, sizeof(struct v4l2_requestbuffers));
    vd->rb.count = NB_BUFFER;
    vd->rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vd->rb.memory = V4L2_MEMORY_MMAP;

    ret = xioctl(vd->fd, VIDIOC_REQBUFS, &vd->rb);

The buffer area applied here is kernel space, and its purpose is to store the video data collected by the camera here. Its control three board axe is still ioctl, instruction and structure. The instruction is VIDIOC_REQBUFS. The structure is v4l2_ The prototype of requestbuffers is as follows:

/*
 *      M E M O R Y - M A P P I N G   B U F F E R S
 */
struct v4l2_requestbuffers {
        __u32                   count;
        __u32                   type;           /* enum v4l2_buf_type */
        __u32                   memory;         /* enum v4l2_memory */
        __u32                   reserved[2];
};

For this structure, the key member variables are: type and memory. The type is still V4L2_BUF_TYPE_VIDEO_CAPTURE, indicating the capture type. Memory is mainly used for memory mapping. Its enumeration prototype is as follows:

 enum v4l2_memory {  
        V4L2_MEMORY_MMAP             = 1,  
        V4L2_MEMORY_USERPTR          = 2,  
        V4L2_MEMORY_OVERLAY          = 3,  
 }; 

Here memory=V4L2_MEMORY_MMAP, indicating that the cache is used for memory mapping.

For v4l2_requestbuffers and a member variable count indicate the number of memory blocks requested. After configuring the structure, the ioctl function will send the information required by the structure to the kernel for memory allocation.

6. Memory mapping

Memory mapping is the key to the data acquisition of the whole usb camera. This operation is also the key to video acquisition and video transmission. Its source code is as follows:

/*
     * map the buffers
     */
    for(i = 0; i < NB_BUFFER; i++) {
        memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
        vd->buf.index = i;
        vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        vd->buf.memory = V4L2_MEMORY_MMAP;
        ret = xioctl(vd->fd, VIDIOC_QUERYBUF, &vd->buf);
        if(ret < 0) {
            perror("Unable to query buffer");
            goto fatal;
        }

        if(debug)
            fprintf(stderr, "length: %u offset: %u\n", vd->buf.length, vd->buf.m.offset);

        vd->mem[i] = mmap(0 /* start anywhere */ ,
                          vd->buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd,
                          vd->buf.m.offset);
        if(vd->mem[i] == MAP_FAILED) {
            perror("Unable to map buffer");
            goto fatal;
        }
        if(debug)
            fprintf(stderr, "Buffer mapped at address %p.\n", vd->mem[i]);
    }

The above functions use the for loop function for memory mapping to map each applied kernel space to the address of the user space, which can facilitate developers to read the data stored there through the address of the user space. The key is three board axe, ioctl, instruction and structure. The instruction is VIDIOC_QUERYBUF, structure: v4l2_buffer, the prototype of the structure is as follows:

struct v4l2_buffer {
        __u32                   index;
        __u32                   type;
        __u32                   bytesused;
        __u32                   flags;
        __u32                   field;
        struct timeval          timestamp;
        struct v4l2_timecode    timecode;
        __u32                   sequence;

        /* memory location */
        __u32                   memory;
        union {
                __u32           offset;
                unsigned long   userptr;
                struct v4l2_plane *planes;
                __s32           fd;
        } m;
        __u32                   length;
        __u32                   reserved2;
        __u32                   reserved;
};

The key of this structure is type and memory. Type determines whether to collect video data, and memory determines how to process the applied memory. Here is mapping processing.

type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

memory = V4L2_MEMORY_MMAP;

Of course, to realize mapping, the most critical skill is the mmap function. Its prototype is as follows:

void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);
mmap Function creates a virtual address to map the physical space,
addr: Specify the starting address of a mapped virtual address. 0 means any starting address.
length: Specifies the length of the mapping
prot: The specified memory protection methods are:
       PROT_EXEC;  Pages may be executed.

       PROT_READ;  Pages may be read.

       PROT_WRITE; Pages may be written.

       PROT_NONE;  Pages may not be accessed.

flag: Parameter determines whether updates to the map can access other processes that map to the same region
offset: File descriptor fd Referenced files(Or other objects)Offset in.

7. Queue buffer

for(i = 0; i < NB_BUFFER; ++i) {
        memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
        vd->buf.index = i;
        vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        vd->buf.memory = V4L2_MEMORY_MMAP;
        ret = xioctl(vd->fd, VIDIOC_QBUF, &vd->buf);
        if(ret < 0) {
            perror("Unable to queue buffer");
            goto fatal;;
        }
    }

The key operation of putting the collected video data into the cache queue is also the three board Axe: ioctl, instruction and structure. The instruction is VIDIOC_QBUF, structure: v4l2_buffer. The key parameters in this structure are:

type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

memory = V4L2_MEMORY_MMAP;

Keywords: C C++ Linux

Added by jamiel on Thu, 16 Dec 2021 07:46:58 +0200