linux中的USB摄像头驱动(应用层)(基于V4L2)

发布时间 2023-08-09 08:33:32作者: 阿风小子

V4L2 是 Video4Linux2 的缩写,是 Linux 内核中的一个视频设备驱动接口。 

 USB-V4L2 初始化流程

1.打开设备节点 open

2.配置参数:分辨率,fps,格式 ioctl

3.请求分配帧缓存->地址映射

4.加入队列中

/**
  ******************************************************************************
  * @file    usbcamera.c
  * @author  CJ
  * @version V1.0
  * @date    2020/8
  * @brief
  ******************************************************************************
  * @attention
  ******************************************************************************
  */
 
#include "usbcamera.h"
#include <sys/types.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <linux/fb.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <dirent.h>
#include <stdlib.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/time.h>
#include <assert.h>
#include <sys/select.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
static UsbCameraSyspara g_cameraSyspara;
 
bool usbCamera_init()
{
    /* 1、打开设备节点 */
    g_cameraSyspara.fd = open("/dev/video0", O_RDWR);
    if(g_cameraSyspara.fd < 0)
    {
        printf("Camera device failed to open !\n");
        return false;
    }
 
    /* 2、定义v4l2_format结构体变量并完成赋值 */
    memset(&g_cameraSyspara.tV4l2Fmt, 0, sizeof(struct v4l2_format));
    g_cameraSyspara.tV4l2Fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;    //视频捕获
    g_cameraSyspara.tV4l2Fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
 
    //设置输出图像尺寸
    g_cameraSyspara.tV4l2Fmt.fmt.pix.width  = 1280;
    g_cameraSyspara.tV4l2Fmt.fmt.pix.height = 720;
    g_cameraSyspara.tV4l2Fmt.fmt.pix.field  = V4L2_FIELD_ANY;    //驱动自行选择合适的格式(逐行、隔行)接收images
 
    /* 3、调用IOCTL完成以上参数配置 */
    if(ioctl(g_cameraSyspara.fd, VIDIOC_S_FMT, &g_cameraSyspara.tV4l2Fmt) != 0)
    {
        printf("tV4l2Fmt failed !\n");
        return false;
    }
    printf("w = %d, h = %d\n", g_cameraSyspara.tV4l2Fmt.fmt.pix.width, g_cameraSyspara.tV4l2Fmt.fmt.pix.height);
    printf("size = %d\n", g_cameraSyspara.tV4l2Fmt.fmt.pix.sizeimage);
    printf("format = %x, nv12 = %x\n", g_cameraSyspara.tV4l2Fmt.fmt.pix.pixelformat, V4L2_PIX_FMT_YUYV);
 
    g_cameraSyspara.RGBData=(unsigned char*)malloc(g_cameraSyspara.tV4l2Fmt.fmt.pix.sizeimage);
 
    /* 4、定义v4l2_request结构体变量并完成赋值 */
    memset(&g_cameraSyspara.tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));
 
    g_cameraSyspara.tV4l2ReqBuffs.count   = 10;                                //帧缓存个数
    g_cameraSyspara.tV4l2ReqBuffs.memory  = V4L2_MEMORY_MMAP;                //该缓存支持映射
    g_cameraSyspara.tV4l2ReqBuffs.type    = V4L2_BUF_TYPE_VIDEO_CAPTURE;    //视频捕获支持
 
    /* 5、调用IOCTL请求分配帧缓存 */
    ioctl(g_cameraSyspara.fd, VIDIOC_REQBUFS, &g_cameraSyspara.tV4l2ReqBuffs);
 
    /* 6、将帧缓存映射至进程空间 */
    unsigned int i;
 
    for(i = 0;i < g_cameraSyspara.tV4l2ReqBuffs.count;i++)
    {
        memset(&g_cameraSyspara.tV4l2Buf, 0, sizeof(struct v4l2_buffer));
 
        g_cameraSyspara.tV4l2Buf.index = i;                                //帧缓存编号
        g_cameraSyspara.tV4l2Buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;    //视频捕获支持
        g_cameraSyspara.tV4l2Buf.memory = V4L2_MEMORY_MMAP;                //内存映射支持
 
        //调用IOCTL获取每个帧缓存的相关信息,其中长度、偏移量将作为mmap的参数
        ioctl(g_cameraSyspara.fd, VIDIOC_QUERYBUF, &g_cameraSyspara.tV4l2Buf);
 
        //映射空间地址
        g_cameraSyspara.MyVideBuf[i] = (unsigned char *)mmap(0, g_cameraSyspara.tV4l2Buf.length, PROT_READ,MAP_SHARED, g_cameraSyspara.fd, g_cameraSyspara.tV4l2Buf.m.offset);
 
    }
 
    /* 7、将帧缓存加入队列循环采集 */
    for (i = 0; i <g_cameraSyspara.tV4l2ReqBuffs.count; i++)
    {
        memset(&g_cameraSyspara.tV4l2Buf, 0, sizeof(struct v4l2_buffer));
        g_cameraSyspara.tV4l2Buf.index = i;
        g_cameraSyspara.tV4l2Buf.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        g_cameraSyspara.tV4l2Buf.memory = V4L2_MEMORY_MMAP;
        ioctl(g_cameraSyspara.fd, VIDIOC_QBUF, &g_cameraSyspara.tV4l2Buf);
    }
 
    struct v4l2_streamparm Stream_Parm;
 
    Stream_Parm.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
    int ret = ioctl(g_cameraSyspara.fd, VIDIOC_G_PARM, &Stream_Parm);
    printf("ret = %d >>!!!!!!!!!!!!!!!!!!: Frame rate: %u/%u\n",ret, Stream_Parm.parm.capture.timeperframe.numerator,
                                     Stream_Parm.parm.capture.timeperframe.denominator);
    Stream_Parm.parm.capture.timeperframe.numerator = 1;
    Stream_Parm.parm.capture.timeperframe.denominator = 15;
    printf("ret = %d >>!!!!!!!!!!!!!!!!!!: Frame rate: %u/%u\n",ret, Stream_Parm.parm.capture.timeperframe.numerator,
                                     Stream_Parm.parm.capture.timeperframe.denominator);
    ret = ioctl(g_cameraSyspara.fd, VIDIOC_S_PARM, &Stream_Parm);
 
    printf("ret = %d >>!!!!!!!!!!!!!!!!!!: Frame rate: %u/%u\n",ret, Stream_Parm.parm.capture.timeperframe.numerator,
                                     Stream_Parm.parm.capture.timeperframe.denominator);
    return true;
}
 
bool openUsbCamera()
{
    int iType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
    /* 启动摄像头开始采集 */
    if(ioctl(g_cameraSyspara.fd, VIDIOC_STREAMON, &iType) == 0)
    {
        return true;
    }
 
    return false;
}
 
bool clsoeUsbCamera()
{
    int iType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
    if(ioctl(g_cameraSyspara.fd, VIDIOC_STREAMOFF, &iType) == 0)
    {
        return true;
    }
 
    return false;
}
 
unsigned char *getUsbCameraImage(unsigned int *size)
{
    fd_set readfds;
    unsigned int ListNum;
 
    FD_ZERO(&readfds);
    FD_SET(g_cameraSyspara.fd,&readfds);
 
    struct timeval tv;
    unsigned int time1, time2;
    gettimeofday(&tv, NULL);
    time1 = tv.tv_sec*1000000+tv.tv_usec;
 
    select(g_cameraSyspara.fd+1, &readfds, NULL, NULL, NULL);
 
    gettimeofday(&tv, NULL);
    time2 = tv.tv_sec*1000000+tv.tv_usec;
    printf("time = %dms\n", (time2-time1)/1000);
 
 
 
    memset(&g_cameraSyspara.tV4l2Buf, 0, sizeof(struct v4l2_buffer));
    g_cameraSyspara.tV4l2Buf.type    = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    g_cameraSyspara.tV4l2Buf.memory  = V4L2_MEMORY_MMAP;
 
    /* read data */
    ioctl(g_cameraSyspara.fd, VIDIOC_DQBUF, &g_cameraSyspara.tV4l2Buf);
    ListNum = g_cameraSyspara.tV4l2Buf.index;    //数据地址 MyVideBuf[ListNum]
 
    memset(&g_cameraSyspara.tV4l2Buf, 0, sizeof(struct v4l2_buffer));
    g_cameraSyspara.tV4l2Buf.index  = ListNum;
    g_cameraSyspara.tV4l2Buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    g_cameraSyspara.tV4l2Buf.memory = V4L2_MEMORY_MMAP;
    ioctl(g_cameraSyspara.fd, VIDIOC_QBUF, &g_cameraSyspara.tV4l2Buf);  //将缓冲区再次放入队列
 
    *size = g_cameraSyspara.tV4l2Fmt.fmt.pix.sizeimage;
    printf("format cnt = %d\n", ListNum);
 
    return g_cameraSyspara.MyVideBuf[ListNum];
}
/**
  ******************************************************************************
  * @file    usbcamera.h
  * @author  CJ
  * @version V1.0
  * @date    2020/8
  * @brief
  ******************************************************************************
  * @attention
  ******************************************************************************
  */
#ifndef USB_CAMERA_H
#define USB_CAMERA_H
 
#include "stdbool.h"
#include <linux/videodev2.h>
 
typedef struct _UsbCameraSyspara
{
    int fd;
    unsigned char *RGBData;              //解码
    struct v4l2_format  tV4l2Fmt;
    struct v4l2_buffer tV4l2Buf;            //缓存区的相关信息
    unsigned char* MyVideBuf[10];            //映射的空间地址
    struct v4l2_requestbuffers tV4l2ReqBuffs;
}UsbCameraSyspara;
 
bool usbCamera_init();
bool openUsbCamera();
bool clsoeUsbCamera();
unsigned char *getUsbCameraImage(unsigned int *size);

#endif