Note: Please indicate the source when reprinting, all rights reserved by the author.Note: This is based on my own understanding,if it conflicts with your principles and ideas, please forgive me, do not criticize.
Environment Description
Ubuntu 16.04 LTS
Introduction
None
V4L2
Introduction to V4L2
Although there are many articles introducing Linux V4L2, I still want to cover some basic concepts.
- 1. v4l2 stands for Video for Linux 2.
- 2. v4l2 supports not only image devices but also audio and other types of devices.
- 3. The prerequisite for using v4l2 is that the Linux kernel has recognized and registered the relevant devices, commonly seen as device files like /dev/videox. (If you are doing image capture, you need to have a camera, and this camera must be recognized by the Linux kernel.)
- 4. As mentioned above, a device must be recognized by the kernel through a driver, and for commonly used image capture, a camera driver is required. To address this, Linux kernel developers have developed a generic driver based on the UVC standard and integrated it into the Linux kernel. Any camera chip that supports the UVC standard can use this driver. (Similar situations exist for non-image devices, but this will not be elaborated here.)
- 5. Generally, the commonly used Linux platform has the UVC driver compiled into the kernel. If you are porting an embedded Linux kernel, please enable the UVC driver option when configuring the kernel options. See the image below:
Overview of the V4L2 Framework
A brief overview of the V4L2 framework (we only need to focus on a few structures; for more in-depth information, refer to the kernel source code).
- 1. struct v4l2_device is the root node in the v4l2 framework, mainly used to manage and traverse other child nodes.
- 2. struct v4l2_subdev is a child node in the v4l2 framework, and there can be multiple v4l2_subdevs under a v4l2_device, mainly distinguishing device types, such as image or audio, etc.
- 3. struct video_device is the structure for specific devices and creates relevant device files under /dev/.
- 4. struct v4l2_buffer provides space for device data exchange.
Principle (taking USB cameras as an example): When a device connects to the kernel, it first initializes the USB according to the USB standard protocol, ultimately completing USB device information detection. Based on the USB device information, the kernel assigns the corresponding driver. Here, the kernel recognizes our USB device as an image device, and it begins to initialize the v4l2_subdev structure, setting the type to graphic image device. Once initialized, the kernel continues to initialize a video_device structure and inserts it into the management linked list of v4l2_subdev. This initialization process involves loading the camera driver, creating device files, etc. At this point, the entire registration process is complete, meaning we can use the v4l2 framework to operate our device. The operation involves calling various ops.
Using V4L2
Using V4L2 (there’s not much to say, as various materials are widely available; I will directly provide the source code).ym_v4l2.c file
/*
FileName:m_v4l2.c
Version:1.5
Description:
Created On: 2017-2-21
Modified date:2017-3-14
Author:Sky
*/
#include <ym_v4l2.h>
intyInitMV4l2(const char * pathname, yMV4L2 * mv4l2){
//mv4l2 = mvl;
//request alloc IMG_BUFF_NUM DATA_BUF size mem
if ( (mv4l2->img_buf = (IMG_BUF *)calloc(IMG_BUFF_NUM, sizeof(IMG_BUF))) == NULL){
printf("calloc failed\n");
return-1;
}
if ( (mv4l2->camera_fd = open(pathname, O_RDWR | O_NONBLOCK)) < 0){//open video device
perror("Open video device faild");
return-1;
}
return0;
}
intyIoctlV4l2(enum yV4l2Cmd cmd,...){
va_list arg;
va_start(arg,cmd);
yMV4L2 *mv4l2;
mv4l2 = va_arg(arg,yMV4L2 *);
va_end(arg);
switch(cmd){
case yVIDIOC_QUERYCAP:
{
if ( ioctl(mv4l2->camera_fd, VIDIOC_QUERYCAP, &mv4l2->cap) < 0){
perror("QUERY VIDEO CAP FAILED");
return-1;
}
printf("DriverName:%s/nCard Name:%s/nBusinfo:%s/nDriverVersion:%u.%u.%u\n",mv4l2->cap.driver,mv4l2->cap.card,mv4l2->cap.bus_info,(mv4l2->cap.version>>16)&0XFF,(mv4l2->cap.version>>8)&0xFF,mv4l2->cap.version&0xFF);
if ( !(mv4l2->cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE) ){
printf("The device is not a video capture\n");
return-1;
}
if ( !(mv4l2->cap.capabilities & V4L2_CAP_STREAMING) ){
printf("The device can not support streaming i/o\n");
return-1;
}
break;
}
case yVIDIOC_ENUM_FMT:
{
CLEAR_MEM(mv4l2->desc_fmt);
mv4l2->desc_fmt.index = 0;
mv4l2->desc_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while( ioctl(mv4l2->camera_fd, VIDIOC_ENUM_FMT,&mv4l2->desc_fmt) == 0 ){
printf("index : %d, format:%s \n", mv4l2->desc_fmt.index,mv4l2->desc_fmt.description);
mv4l2->desc_fmt.index++;
}
break;
}
case yVIDIOC_S_FMT:
{
// set data format for dev
mv4l2->stream_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mv4l2->stream_fmt.fmt.pix.width = IMAGE_WIDTH;
mv4l2->stream_fmt.fmt.pix.height = IMAGE_HEIGHT;
mv4l2->stream_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
//mv4l2->stream_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
mv4l2->stream_fmt.fmt.pix.field = V4L2_FIELD_ANY;
if ( ioctl(mv4l2->camera_fd, VIDIOC_S_FMT, &mv4l2->stream_fmt) ){
perror("Set data format failed");
return-1;
}
break;
}
case yVIDIOC_G_FMT:
{
CLEAR_MEM(mv4l2->stream_fmt);
mv4l2->stream_fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(mv4l2->camera_fd,VIDIOC_G_FMT,&mv4l2->stream_fmt);
printf("Currentdata format information: width:%d height:%d\n",mv4l2->stream_fmt.fmt.pix.width,mv4l2->stream_fmt.fmt.pix.height);
break;
}
case yVIDIOC_REQBUFS:
{
//bzero(&reqbuf, sizeof(reqbuf));
CLEAR_MEM(mv4l2->reqbuf);
mv4l2->reqbuf.count = IMG_BUFF_NUM;
mv4l2->reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mv4l2->reqbuf.memory = V4L2_MEMORY_MMAP;
if ( ioctl(mv4l2->camera_fd, VIDIOC_REQBUFS, &mv4l2->reqbuf) < 0 ){
perror("ioctl REQBUFS failed");
return-1;
}
break;
}
case yVIDIOC_STREAMON:
{
mv4l2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if ( ioctl(mv4l2->camera_fd, VIDIOC_STREAMON,&mv4l2->type)< 0){
perror("Failed to ioctl:VIDIOC_STREAMON");
return-1;
}
break;
}
case yVIDIOC_STREAMOFF:
{
mv4l2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if ( ioctl(mv4l2->camera_fd, VIDIOC_STREAMOFF,&mv4l2->type)< 0){
perror("Failed to ioctl:VIDIOC_STREAMOFF");
return-1;
}
break;
}
case yVIDIOC_S_PARM:
{
CLEAR_MEM(mv4l2->stream_parm);
mv4l2->stream_parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mv4l2->stream_parm.parm.capture.timeperframe.numerator = 1;
mv4l2->stream_parm.parm.capture.timeperframe.denominator = 10;
if ( ioctl(mv4l2->camera_fd, VIDIOC_S_PARM, &mv4l2->stream_parm) < 0){
perror("Failed to ioctl:VIDIOC_S_PARM");
return-1;
}
break;
}
case yVIDIOC_G_PARM:
{
CLEAR_MEM(mv4l2->stream_parm);
mv4l2->stream_parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if ( ioctl(mv4l2->camera_fd, VIDIOC_G_PARM, &mv4l2->stream_parm) < 0){
perror("Failed to ioctl:VIDIOC_G_PARM");
return-1;
}
if ( mv4l2->stream_parm.parm.capture.capability == V4L2_CAP_TIMEPERFRAME ){
printf("This Video Support Set Fps,Now-Fps is : %d\n",mv4l2->stream_parm.parm.capture.timeperframe.denominator);
}
else{
printf("This Video Un-Support Set Fps\n");
}
break;
}
case yVIDIOC_DQBUF:
{
//bzero(&normal_buf, sizeof(normal_buf));
CLEAR_MEM(mv4l2->normal_buf);
mv4l2->normal_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mv4l2->normal_buf.memory = V4L2_MEMORY_MMAP;
if ( ioctl(mv4l2->camera_fd, VIDIOC_DQBUF, &mv4l2->normal_buf) < 0){
perror("Failed to ioctl:VIDIOC_DQBUF");
return-1;
}
break;
}
case yVIDIOC_QBUF:
{
if ( ioctl(mv4l2->camera_fd, VIDIOC_QBUF, &mv4l2->normal_buf) < 0){
perror("Failed to ioctl:VIDIOC_QBUF");
return-1;
}
break;
}
case yMMAPTOVEDIOBUF:
{
for ( int i=0; i < IMG_BUFF_NUM; i++ ){
//bzero(&normal_buf, sizeof(normal_buf));
CLEAR_MEM(mv4l2->normal_buf);
mv4l2->normal_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mv4l2->normal_buf.memory = V4L2_MEMORY_MMAP;
mv4l2->normal_buf.index = i;
//get kernel cache information
if ( ioctl(mv4l2->camera_fd, VIDIOC_QUERYBUF,&mv4l2->normal_buf) < 0){
perror("Failed to ioctl:VIDIOC_QUERYBUF");
return-1;
}
mv4l2->img_buf[i].len = mv4l2->normal_buf.length;
mv4l2->img_buf[i].start = mmap(NULL,mv4l2->normal_buf.length,
PROT_READ|PROT_WRITE,
MAP_SHARED,mv4l2->camera_fd,
mv4l2->normal_buf.m.offset);
if ( MAP_FAILED == mv4l2->img_buf[i].start){
perror("Failed to mmap");
return-1;
}
}
break;
}
case yPUTVEDIOALLBUFTOQUEUE:
{
//bzero(&normal_buf, sizeof(normal_buf));
for ( int i = 0; i < IMG_BUFF_NUM;i++){
CLEAR_MEM(mv4l2->normal_buf);
mv4l2->normal_buf.index = i;
mv4l2->normal_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mv4l2->normal_buf.memory = V4L2_MEMORY_MMAP;
if ( ioctl(mv4l2->camera_fd, VIDIOC_QBUF,&mv4l2->normal_buf) < 0){
perror("Failed to ioctl:VIDIOC_QBUF");
return-1;
}
}
break;
}
case yUNMMAPTOVEDIOBUF:
{
for (int i = 0; i < IMG_BUFF_NUM; i++){
if ( munmap(mv4l2->img_buf[i].start,mv4l2->img_buf[i].len) < 0){
perror("Failed to munmap");
return-1;
}
}
break;
}
default :
{
return-1;
break;
}
}
return0;
}
intyDestroyMV4l2(yMV4L2 *mv4l2){
//StopStream();
yIoctlV4l2(yVIDIOC_STREAMOFF,mv4l2);
//UnMMapToVedioBUf(mv4l2);
yIoctlV4l2(yUNMMAPTOVEDIOBUF,mv4l2);
close(mv4l2->camera_fd);
free(mv4l2->img_buf);
return0;
}
ym_v4l2.h
/*
FileName:ym_v4l2.h
Version:1.5
Description:
Created On: 2017-2-21
Modified date:2017-3-14
Author:Sky
*/
#ifndef _YM_V4L2_H
#define _YM_V4L2_H
#ifdef __cplusplus
#if __cplusplus
extern"C"{
#endif
#endif /* __cplusplus */
#include <ym_v4l2_config.h> //open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> //memset
#include <string.h> //v4l2
#include <linux/videodev2.h> //errno
#include <errno.h> //perror
#include <stdio.h> //calloc,free
#include <stdlib.h> //close
#include <unistd.h> //ioctl
#include <sys/ioctl.h> //mmap,munmap
#include <sys/mman.h>
#include <stdarg.h>
#define CLEAR_MEM(x) memset(&(&(x)),0,sizeof(x))
enum yV4l2Cmd{
yVIDIOC_QUERYCAP = 0,//Get Camera Capability
yVIDIOC_ENUM_FMT = 1,//Get Camera all support-format
yVIDIOC_S_FMT = 2,//Set Img Format
yVIDIOC_G_FMT = 3,//Get Img Format
yVIDIOC_REQBUFS = 4,//Req Video buf
yVIDIOC_STREAMON = 5,//Start stream
yVIDIOC_STREAMOFF = 6,//Stop stream
yVIDIOC_S_PARM = 7,//Set Fps info
yVIDIOC_G_PARM = 8,//Get Fps info
yVIDIOC_DQBUF = 9,//delete buf from out queue
yVIDIOC_QBUF = 10,//put buf to in queue
yMMAPTOVEDIOBUF = 11,
yUNMMAPTOVEDIOBUF = 12,
yPUTVEDIOALLBUFTOQUEUE = 13,
};
typedefstruct {
void * start;
long len;
} IMG_BUF;
typedefstruct ymv4l2{
int camera_fd;//camera file descriptor
IMG_BUF *img_buf;//img buf head
struct v4l2_buffer normal_buf;
struct v4l2_fmtdesc desc_fmt;
struct v4l2_capability cap;
struct v4l2_format stream_fmt;
struct v4l2_requestbuffers reqbuf;
struct v4l2_streamparm stream_parm;
enum v4l2_buf_type type;
}yMV4L2;
intyInitMV4l2(const char * pathname, yMV4L2 * mvl);
intyDestroyMV4l2(yMV4L2 * mvl);
intyIoctlV4l2(enum yV4l2Cmd cmd,...);
//int OpenCamera(const char * pathname);
//int GetCapability(void);
//int GetAllSupportFormat(void);
//int SetFrameInfo(void);//to set fps
//int GetFrameInfo(void);
//int SetImgFormat(void);
//int GetImgFormat(void);
//int RequestVedioBuf(void);
//int MMapToVedioBuf(void);
//int PutVedioBufToQueue(void);
//int UnMMapToVedioBUf(void);
//int StartStream(void);
//int StopStream(void);
//int ConfigV4l2(void);
//int GetVedioBufFromQueue(void);
//int PutVedioBufToQueue(void);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */
#endif
Postscript
None
References
- • None
Donations, subscriptions, favorites, throwing bananas, coins, please follow the public account
Note: Please respect originality, do not criticize if you dislike.Note: Please indicate the source when reprinting, all rights reserved by the author.Note: If you have questions, please leave a message, I will reply as soon as I see it.