V4L2采集视频

/**
 * 1. 打开设备
 * 2. 获取设备能力
 * 3. 设置视频格式
 * 4. 进行内核空间和用户空间的内存映射
 * 5. 开启视频流
 * 6. 获取视频流
 * 7. 关闭视频流
 * 8. 取消映射
 * 9. 关闭设备
 * 
 * 
 * 
*/

#include 
#include 
#include 

#include 
#include 

#include 

#include 

#include 

#include 
#include 
#include 

const int WIDTH = 480;
const int HEIGHT = 640;

const char deviceNmae[] = "/dev/video0";

typedef struct VideoBuffer
{ //定义一个结构体来映射每个缓冲帧
    void *start;
    size_t length;
} VideoBuffer;


int main(int argc, char const *argv[])
{
    int fd = open(deviceNmae, O_RDWR);
    if (fd < 0)
    {
        perror("open()");
        return -1;
    }

    /*1. 查询能力 */
    struct v4l2_capability stCap;
    int s32Ret = ioctl(fd, VIDIOC_QUERYCAP, &stCap);
    if (s32Ret < 0)
    {
        perror("VIDIOC_QUERYCAP()");
        return -2;
    }
    else
    {
        printf("Driver Name:%s\nCard Name:%s\nBus info:%s\nDriver Version:%u.%u.%u\n",
               stCap.driver, stCap.card, stCap.bus_info, (stCap.version >> 16) & 0XFF,
               (stCap.version >> 8) & 0XFF, stCap.version & 0XFF);
    }

    //获取成功,检查是否有视频捕获功能
    if(!(stCap.capabilities & V4L2_CAP_VIDEO_CAPTURE)){
        fprintf(stderr, "%s is no video capture device\n",deviceNmae);
        return -1;
    }

    /*2. 获取当前驱动支持的视频格式 */
    struct v4l2_fmtdesc stFmtdesc;
    stFmtdesc.index = 0;
    stFmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    printf("Support format:\n");
    while (ioctl(fd, VIDIOC_ENUM_FMT, &stFmtdesc) != -1)
    {
        printf("\t%d.%s\n", stFmtdesc.index + 1, stFmtdesc.description);
        stFmtdesc.index++;
    }

    /* 3. 设置当前视频捕获格式 */
    struct v4l2_format stFormat;
    stFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    stFormat.fmt.pix.width = WIDTH;
    stFormat.fmt.pix.height = HEIGHT;
    stFormat.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;

    s32Ret = ioctl(fd, VIDIOC_S_FMT, &stFormat);
    if (s32Ret < 0)
    {
        perror("VIDIOC_S_FMT");
        return -3;
    }

    /* 设置视频流参数 */
    struct v4l2_streamparm stream_parm;
    stream_parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    stream_parm.parm.capture.timeperframe.denominator = 10; //fps
    stream_parm.parm.capture.timeperframe.numerator = 1;
    s32Ret = ioctl(fd, VIDIOC_S_PARM, &stream_parm);
    if (s32Ret < 0)
    {
        printf("Unable to set frame rate\n");
        goto exit;
    }

    /* 4. 向驱动申请视频流数据的帧缓冲区,进行内核空间和用户控件的内存映射 */
    struct v4l2_requestbuffers stReq;
    stReq.count = 4;
    stReq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    stReq.memory = V4L2_MEMORY_MMAP;
    s32Ret = ioctl(fd, VIDIOC_REQBUFS, &stReq);
    if (s32Ret < 0)
    {
        perror("VIDIOC_REQBUFS");
        goto exit;
    }

    
    VideoBuffer* buffers = calloc( stReq.count, sizeof(*buffers) );
    struct v4l2_buffer stBuf;
    for (int i_cnt = 0; i_cnt < 4; i_cnt++)
    {
        stBuf.index = i_cnt;
        stBuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        stBuf.memory = V4L2_MEMORY_MMAP;
        s32Ret = ioctl(fd, VIDIOC_QUERYBUF, &stBuf);
        if (s32Ret)
        {
            perror("VIDIOC_QUERYBUF");
            goto exit;
        }
        printf("%d\n",stBuf.length);

        buffers[i_cnt].length = stBuf.length;
        buffers[i_cnt].start = (char *)mmap(NULL, stBuf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //隐射一块用户空间到内核空间 此时的 buffers[] 和已经和申请的四个v4l2_buffer,已经对应。
        if (buffers[i_cnt].start == MAP_FAILED)
        {
            perror("mmap()");
            goto exit;
        }

        //将刚申请好的v4l2_buffer 加入可捕获视频的队列
        s32Ret = ioctl(fd, VIDIOC_QBUF, &stBuf);
        if (s32Ret < 0)
        {
            perror("VIDIOC_QBUF");
            goto exit;
        }
    }

    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    s32Ret = ioctl(fd, VIDIOC_STREAMON, &type);
    if (s32Ret < 0)
    {
        perror("VIDIOC_STREAMON");
        goto exit;
    }

    FILE *file = fopen("my.yuv", "wb");

    int s32Cnt = 0;
    while (s32Cnt < 500)
    {
        /* 6. 获取视频流 */
        stBuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        s32Ret = ioctl(fd, VIDIOC_DQBUF, &stBuf);
        if (s32Ret < 0)
        {
            perror("VIDIOC_DQBUF");
            goto exit;
        }

        s32Ret = fwrite(buffers[stBuf.index].start, stBuf.bytesused, 1, file);
        // char buff[640*480*2] = {0};
        // s32Ret = fwrite(buff, 640*480*2, 1, file);
        if (s32Ret < 0)
        {
            perror("fwrite()");
            goto exit;
        }
        printf("[%d:%d]\n", s32Cnt,stBuf.bytesused);

        s32Ret = ioctl(fd, VIDIOC_QBUF, &stBuf);
        if (s32Ret < 0)
        {
            perror("VIDIOC_QBUF");
            goto exit;
        }

        s32Cnt++;
    }

    s32Ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
    if (s32Ret < 0)
    {
        perror("VIDIOC_STREAMON");
        goto exit;
    }

exit:

    fclose(file);
    for (int i = 0; i < 4; i++)
    {
        munmap(buffers[i].start, buffers[i].length);
    }

    close(fd);

    return 0;
}

为啥我采集处理的视频,那么多的彩色呢?发愁人啊。

V4L2采集视频_第1张图片
V4L2采集视频_第2张图片
V4L2采集视频_第3张图片

ffmpeg -video_size 640*480 -pixel_format yuv422p  -framerate 10 -i my.yuv   -vcodec h264 outpu.mp4

使用ffmpeg 将分辨率为640*480的yuv422p,10帧的视频编码压缩为mp4

推荐阅读:v4l2的学习建议和流程解析

你可能感兴趣的:(V4L2采集视频)