V4L2框架获取CSI摄像头视频流

#include "stdio.h"
#include "linux/videodev2.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "string.h"
#include 
#include 

#define IMAGEWIDTH 240
#define IMAGEHEIGHT 240
 
#define TRUE 1
#define FALSE 0
 
#define FILE_VIDEO1 "/dev/video11"
 
static int fd;                          //设备描述符
struct v4l2_streamparm setfps;          //结构体v4l2_streamparm来描述视频流的属性
struct v4l2_capability cap;             //取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等
struct v4l2_fmtdesc fmtdesc;            //枚举设备所支持的image format:  VIDIOC_ENUM_FMT
struct v4l2_format fmt,fmtack;          //子结构体struct v4l2_pix_format设置摄像头采集视频的宽高和类型:V4L2_PIX_FMT_YYUV V4L2_PIX_FMT_YUYV
struct v4l2_requestbuffers req;         //向驱动申请帧缓冲的请求,里面包含申请的个数
struct v4l2_buffer buf;                 //代表驱动中的一帧
enum   v4l2_buf_type type;              //帧类型

 
typedef struct {
    void *start;
    int length;
}buftype;

buftype *usr_buf;

int main(void){
	char image_name[20];
    unsigned int n_buffers;

     //1. open video file
    if ((fd = open(FILE_VIDEO1, O_RDWR)) == -1){//打开设备采用阻塞式,等到设备捕获信息之后才会返回给应用;非阻塞式即使尚未捕捉到信息,驱动仍然会把缓存东西返回给应用程序。
        printf("Opening video device error\n");
        return FALSE;
    }

    //2. set capabilities
    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1){  //查询视频设备的功能
        printf("unable Querying Capabilities\n");
        return FALSE;
    }
    else
    {
        printf( "Driver Caps:\n"
            "  Driver: \"%s\"\n"
            "  Card: \"%s\"\n"
            "  Bus: \"%s\"\n"
            "  Version: %d\n"
            "  Capabilities: %x\n",
            cap.driver,
            cap.card,
            cap.bus_info,
            cap.version,
            cap.capabilities);
    }
    if((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE){
        printf("V4L2_CAP_VIDEO_CAPTURE   Camera device %s: support capture\n",FILE_VIDEO1);
    }
    if((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING){
        printf("V4L2_CAP_STREAMING       Camera device %s: support streaming.\n",FILE_VIDEO1);
    }

     //3. set fmt
    fmtdesc.index = 0;
    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    printf("Support format: \n");
    while(ioctl(fd,VIDIOC_ENUM_FMT, &fmtdesc) != -1){    // 获取当前视频设备支持的视频格式
        printf("\t%d. %s\n",fmtdesc.index+1,fmtdesc.description);
        fmtdesc.index++;
    }

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    fmt.fmt.pix.width = IMAGEWIDTH;                  
    fmt.fmt.pix.height = IMAGEHEIGHT;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;    //V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUYV
    fmt.fmt.pix.field = V4L2_FIELD_NONE;

    if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1){    // 设置视频设备的视频数据格式,例如设置视频图像数据的长、宽,图像格式(MJPEG、YUYV格式)
        perror("VIDIOC_S_FMT");
        return FALSE;
    }
    if(ioctl(fd,VIDIOC_G_FMT,&fmt) == -1){       //获取当前视频设备捕获图像格式
        printf("Unable to get format\n");
        return FALSE;
    }
    else
    {
        printf("fmt.type:\t%d\n",fmt.type);    //可以输出图像的格式
        printf("pix.pixelformat:\t%c%c%c%c\n",fmt.fmt.pix.pixelformat & 0xFF,(fmt.fmt.pix.pixelformat >> 8) & 0xFF,\
            (fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
        printf("pix.width:\t%d\n", fmt.fmt.pix.width);
        printf("pix.height:\t%d\n",fmt.fmt.pix.height);
        printf("pix.field:\t%d\n",fmt.fmt.pix.field);
    }
    #if 0
    memset(&setfps, 0, sizeof(setfps));

    setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    setfps.parm.capture.timeperframe.numerator = 1;
    setfps.parm.capture.timeperframe.denominator = 15;
    if (ioctl(fd, VIDIOC_S_PARM, &setfps) == -1)    
    {
    	perror("VIDIOC_S_PARM");
        return FALSE;
    }
    #endif
    //4. request for 4 buffers
    req.count = 4;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    req.memory = V4L2_MEMORY_MMAP;
    if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1)    //请求若干个帧缓冲区,开启内存映射或用户指针I/O
    {
        printf("Requesting Buffer error\n");
        return FALSE;
    }

    //5. mmap for buffers
    usr_buf = (buftype*)calloc(req.count, sizeof(buftype));
    if(!usr_buf){
        printf("Out of memory\n");
        return FALSE;
    }

	struct v4l2_plane planes[2];
	memset(&planes[0], 0, sizeof(planes[0]));
    memset(&planes[1], 0, sizeof(planes[1]));

    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.m.planes = planes;
    buf.length = 2;

    for(n_buffers = 0; n_buffers < req.count; n_buffers++)
    {

        buf.index = n_buffers;
        if(ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1){    //查询已经分配的V4L2的视频缓冲区的相关信息,包括视频缓冲区的使用状态、在内核空间的偏移地址、缓冲区长度等。
            perror("line 144");
            return FALSE;
        }

        usr_buf[n_buffers].length = buf.length;
        usr_buf[n_buffers].start = (unsigned char*)mmap (NULL, buf.m.planes[0].length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.planes[0].m.mem_offset);    //将申请到的缓冲区映射到用户空间

        if(usr_buf[n_buffers].start == MAP_FAILED)
        {
            perror("line 159");
            return FALSE;
        }
        printf("Length: %d\nAddress: %p\n", usr_buf[n_buffers].length, usr_buf[n_buffers].start);
        printf("Image Length: %d\n", buf.bytesused);
    }

     //6. queue
    //在 driver 内部管理着两个 buffer queues ,一个输入队列,一个输出队列。
    //对于 capture device 来说,当输入队列中的 buffer 被塞满数据以后会自动变为输出队列,
    //等待调用 VIDIOC_DQBUF 将数据进行处理以后重新调用 VIDIOC_QBUF 将 buffer 重新放进输入队列.
    for(n_buffers = 0; n_buffers 

你可能感兴趣的:(c语言,开源,学习,linux,单片机)