V4L2(Video For Linux Two) 是内核提供给应用程序访问音、视频驱动的统一接口。V4L2 的相关定义包含在头文件
基本参照:
https://blog.csdn.net/li_wen01/article/details/53557949
大部分代码搬过来用了。
在此记录下。
打开视频设备后,可以设置该视频设备的属性,例如裁剪、缩放等。这一步是可选的。在Linux编程中,一般使用ioctl函数来对设备的I/O通道进行管理:
extern int ioctl (int __fd, unsigned long int __request, …) __THROW;
__fd:设备的ID,例如刚才用open函数打开视频通道后返回的cameraFd;
__request:具体的命令标志符。
在进行V4L2开发中,一般会用到以下的命令标志符:
VIDIOC_REQBUFS:分配内存
VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
VIDIOC_QUERYCAP:查询驱动功能
VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式
VIDIOC_S_FMT:设置当前驱动的频捕获格式
VIDIOC_G_FMT:读取当前驱动的频捕获格式
VIDIOC_TRY_FMT:验证当前驱动的显示格式
VIDIOC_CROPCAP:查询驱动的修剪能力
VIDIOC_S_CROP:设置视频信号的边框
VIDIOC_G_CROP:读取视频信号的边框
VIDIOC_QBUF:把数据从缓存中读取出来
VIDIOC_DQBUF:把数据放回缓存队列
VIDIOC_STREAMON:开始视频显示函数
VIDIOC_STREAMOFF:结束视频显示函数
VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PAL或NTSC。
这些IO调用,有些是必须的,有些是可选择的。
我的摄像头支持的型号:
support device 1.YUV 4:2:2 (YUYV)
support device 2.MJPEG
整理代码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define VIDEO_NAME "/dev/video0"
#define TEST_STREAM_SAVE_PATH "/app/sd"
#define BUFFER_NUM (4)
#define V4L2_BUF_TYPE (V4L2_BUF_TYPE_VIDEO_CAPTURE)
static int g_video_fd;
typedef struct{
void *start;
int length;
}BUFTYPE;
BUFTYPE *usr_buf;
static unsigned int n_buffer = 0;
static int HI_PDT_Camera_Open(void)
{
struct v4l2_input inp;
int i = 0;
int ret = -1;
g_video_fd = open(VIDEO_NAME, O_RDWR | O_NONBLOCK,0);
if(g_video_fd < 0)
{
printf("%s open failed ! \n", VIDEO_NAME);
return ret;
};
for(i=0;i<16;i++)
{
inp.index = i;
if (-1 == ioctl (g_video_fd, VIDIOC_S_INPUT, &inp))
{
printf("VIDIOC_S_INPUT failed %d !\n",i);
}
else
{
printf("VIDIOC_S_INPUT success %d !\n",i);
ret = 0;
break;
}
}
return ret;
}
// close
void HI_PDT_Camera_Close(int video_fd)
{
unsigned int i;
for(i = 0;i < n_buffer; i++)
{
if(-1 == munmap(usr_buf[i].start,usr_buf[i].length))
{
exit(-1);
}
}
if(NULL != usr_buf)
free(usr_buf);
if(video_fd >0)
close(video_fd);
return;
}
/*set video capture ways(mmap)*/
int HI_PDT_Init_mmap(int fd)
{
/*to request frame cache, contain requested counts*/
struct v4l2_requestbuffers reqbufs;
memset(&reqbufs, 0, sizeof(reqbufs));
reqbufs.count = BUFFER_NUM; /*the number of buffer*/
reqbufs.type = V4L2_BUF_TYPE;
reqbufs.memory = V4L2_MEMORY_MMAP;
if(-1 == ioctl(fd,VIDIOC_REQBUFS,&reqbufs))
{
perror("Fail to ioctl 'VIDIOC_REQBUFS'");
return -1;
}
n_buffer = reqbufs.count;
printf("n_buffer = %d\n", n_buffer);
usr_buf = calloc(reqbufs.count, sizeof(BUFTYPE));
if(usr_buf == NULL)
{
printf("Out of memory\n");
return -1;
}
/*map kernel cache to user process*/
for(n_buffer = 0; n_buffer < reqbufs.count; n_buffer++)
{
//stand for a frame
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffer;
/*check the information of the kernel cache requested*/
if(-1 == ioctl(fd,VIDIOC_QUERYBUF,&buf))
{
perror("Fail to ioctl : VIDIOC_QUERYBUF");
return -1;
}
usr_buf[n_buffer].length = buf.length;
usr_buf[n_buffer].start = (char *)mmap(NULL,buf.length,PROT_READ | PROT_WRITE,MAP_SHARED, fd,buf.m.offset);
if(MAP_FAILED == usr_buf[n_buffer].start)
{
perror("Fail to mmap");
return -1;
}
}
return 0;
}
static int HI_PDT_Set_Format(int video_fd)
{
struct v4l2_format tv_fmt; /* frame format */
/*set the form of camera capture data*/
tv_fmt.type = V4L2_BUF_TYPE; /*v4l2_buf_typea,camera must use V4L2_BUF_TYPE_VIDEO_CAPTURE*/
tv_fmt.fmt.pix.width = 640;
tv_fmt.fmt.pix.height = 480;
tv_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
tv_fmt.fmt.pix.field = V4L2_FIELD_NONE; /*V4L2_FIELD_NONE*/
if (ioctl(video_fd, VIDIOC_S_FMT, &tv_fmt)< 0)
{
fprintf(stderr,"VIDIOC_S_FMT set err\n");
return -1;
}
return 0;
}
static int HI_PDT_Init_Camera(int video_fd)
{
struct v4l2_capability cap; /* decive fuction, such as video input */
struct v4l2_fmtdesc fmtdesc; /* detail control value */
int ret = 0;
if(video_fd <=0)
return -1;
/*show all the support format*/
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.index = 0 ; /* the number to check */
fmtdesc.type=V4L2_BUF_TYPE;
/* check video decive driver capability */
if(ret=ioctl(video_fd, VIDIOC_QUERYCAP, &cap)<0)
{
fprintf(stderr, "fail to ioctl VIDEO_QUERYCAP \n");
return -1;
}
/*judge wherher or not to be a video-get device*/
if(!(cap.capabilities & V4L2_BUF_TYPE))
{
fprintf(stderr, "The Current device is not a video capture device \n");
return -1;
}
/*judge whether or not to supply the form of video stream*/
if(!(cap.capabilities & V4L2_CAP_STREAMING))
{
printf("The Current device does not support streaming i/o\n");
return -1;
}
printf("\ncamera driver name is : %s\n",cap.driver);
printf("camera device name is : %s\n",cap.card);
printf("camera bus information: %s\n",cap.bus_info);
/*display the format device support*/
printf("\n");
while(ioctl(video_fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
{
printf("support device %d.%s\n",fmtdesc.index+1,fmtdesc.description);
fmtdesc.index++;
}
printf("\n");
return 0;
}
int HI_PDT_start_capture(int fd)
{
unsigned int i;
enum v4l2_buf_type type;
/*place the kernel cache to a queue*/
for(i = 0; i < n_buffer; i++)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if(-1 == ioctl(fd, VIDIOC_QBUF, &buf))
{
perror("Fail to ioctl 'VIDIOC_QBUF'");
exit(EXIT_FAILURE);
}
}
type = V4L2_BUF_TYPE;
if(-1 == ioctl(fd, VIDIOC_STREAMON, &type))
{
printf("i=%d.\n", i);
perror("VIDIOC_STREAMON");
close(fd);
exit(EXIT_FAILURE);
}
return 0;
}
int HI_PDT_process_image(void *addr, int length)
{
FILE *fp;
static int num = 0;
char image_name[64] = {0};
sprintf(image_name, TEST_STREAM_SAVE_PATH"/%d.yuv", num++);
if((fp = fopen(image_name, "w")) == NULL)
{
perror("Fail to fopen \n");
exit(EXIT_FAILURE);
}
fwrite(addr, length, 1, fp);
usleep(500);
fclose(fp);
return 0;
}
int HI_PDT_read_frame(int fd)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE;
buf.memory = V4L2_MEMORY_MMAP;
//put cache from queue
if(-1 == ioctl(fd, VIDIOC_DQBUF,&buf))
{
perror("Fail to ioctl 'VIDIOC_DQBUF'");
return -1;
}
if(buf.index >= n_buffer)
return -1;
//read process space's data to a file
HI_PDT_process_image(usr_buf[buf.index].start, usr_buf[buf.index].length);
if(-1 == ioctl(fd, VIDIOC_QBUF,&buf))
{
perror("Fail to ioctl 'VIDIOC_QBUF'");
return -1;
}
return 0;
}
static int HI_PDT_Stop_Capture(int fd)
{
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE;
if(-1 == ioctl(fd,VIDIOC_STREAMOFF,&type))
{
perror("Fail to ioctl 'VIDIOC_STREAMOFF' \n");
return -1;
}
return 0;
}
int HI_PDT_mainloop(int fd)
{
int count = 10;
while(count-- > 0)
{
while(1)
{
fd_set fds;
struct timeval tv;
int r;
FD_ZERO(&fds);
FD_SET(fd,&fds);
/*Timeout*/
tv.tv_sec = 2;
tv.tv_usec = 0;
r = select(fd + 1,&fds,NULL,NULL,&tv);
if(-1 == r)
{
if(EINTR == errno)
continue;
perror("Fail to select \n");
return -1;
}
if(0 == r)
{
fprintf(stderr,"select Timeout \n");
return -1;
}
if(HI_PDT_read_frame(fd) == 0)
break;
}
}
return 0;
}
int HI_PDT_UVC_Init(HI_VOID)
{
int s32Ret =0;
// 1 open device
s32Ret = HI_PDT_Camera_Open();
if(s32Ret <0)
{
printf("HI_PDT_Camera_Open failed ! \n");
return -1;
}
// Check and set device properties set frame
s32Ret = HI_PDT_Init_Camera(g_video_fd);
if(s32Ret <0)
{
printf("HI_PDT_Camera_Open failed ! \n");
HI_PDT_Camera_Close(g_video_fd);
return -1;
}
HI_PDT_Set_Format(g_video_fd);
// Apply for a video buffer
HI_PDT_Init_mmap(g_video_fd);
//
HI_PDT_start_capture(g_video_fd);
HI_PDT_mainloop(g_video_fd);
HI_PDT_Stop_Capture(g_video_fd);
return 0;
}
int HI_PDT_UVC_DeInit(HI_VOID)
{
HI_PDT_Camera_Close(g_video_fd);
HI_PDT_Stop_Capture(g_video_fd);
return 0;
}