V4L2 Video Capture - 01

本文介绍通过Linux V4L2接口进行视频采集的过程,包括打开视频设备、设定视频格式与属性、分配内存、处理采集数据等步骤。重点讲解了ioctl命令的具体应用及不同采集方式的特点。

http://my.oschina.net/tufu/blog/174134

 

 

1.打开视频设备 
  int cameraFd = open("/dev/video0", O_RDWR | O_NONBLOCK, 0); 
2.设定视频设备属性及采集方式 
  int ioctl(int __fd, unsigned long int __request, .../*args*/); 
  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 
  
  1) 检查当前视频设备支持的标准 
     v4l2_std_id std; 
     do{ 
        ret = ioctl(fd, VIDIOC_QUERYSTD, &std); 
     } while(ret==-1 && errno==EAGAIN); 
     switch(std){ 
        case V4L2_STD_NTSC: 
            // 
        case V4L2_STD_PAL: 
        // 
     } 
     
  2)设置视频捕获格式 
     当检测完视频设备支持的标准后,还需要设定视频捕获格式 
     struct v4l2_format fmt; 
     memset(&fmt, 0, sizeof(fmt)); 
     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
     fmt.fmt.pix.width = 720; 
     fmt.fmt.pix.height = 576; 
     fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; 
     fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; 
     if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1){ 
        return -1; 
     } 
     
     struct v4l2_format{ 
        enum v4l2_buf_type type; // 数据流类型 V4L2_BUF_TYPE_VIDEO_CAPTURE 
        union { 
            struct v4l2_pix_format pix; 
            struct v4l2_window win; 
            struct v4l2_vbi_format vbi; 
            __u8 raw_data[200]; 
        }         
     }; 
     
     struct v4l2_pix_format 
    { 
        __u32                   width;         // 宽,必须是16的倍数 
        __u32                   height;        // 高,必须是16的倍数 
        __u32                   pixelformat;   // 视频数据存储类型,例如是YUV4:2:2还是RGB 
        enum v4l2_field         field; 
        __u32                   bytesperline;     
        __u32                   sizeimage; 
        enum v4l2_colorspace    colorspace; 
        __u32                   priv;        
    }; 
  3)分配内存 
    struct v4l2_requestbuffers req; 
    if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1){ 
        return -1; 
    } 
     
    struct v4l2_requestbuffers{ 
        __u32 count; 
        enum v4l2_buf_type type; 
        enum v4l2_memory memory; //V4L2_MEMORY_MMAP 或 V4L2_MEMORY_USERPTR 
        __u32 reserved[2]; 
    }; 
  
  4)获取并记录缓存的物理空间 
    使用VIDIOC_QUERYBUF 获取缓存地址 
    mmap 函数转换成应用程序中的绝对地址 
    将缓存放入缓存队列 
    typedef struct VideoBuffer{ 
        void *start; 
        size_t length; 
    } VideoBuffer; 
    VideoBuffer *buffers = calloc(req.count, sizeof(VideoBuffer)); 
    struct v4l2_buffer buf; 
    for (numBufs=0; numBufs<req.count; ++numBufs){ 
        memset(&buf, 0, sizeof(buf)); 
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
        buf.memory = V4L2_MEMORY_MMAP; 
        buf.index = numBufs; 
        //读取缓存 
        if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1){ 
            return -1; 
        } 
        buffers[numBufs].length = buf.length; 
        //转换成相对地址 
        buffers[numBufs].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); 
        if (buffers[numBufs].start == MAP_FAILED){ 
            return -1; 
        } 
        //放入缓存队列 
        if (ioctl(fd, VIDIOC_QBUF, &buf) == -1){ 
            return -1; 
        } 
    } 

  5)采集方式 
    一共有三种视频采集方式:使用read、write方式;内存映射方式和用户指针模式。 
    read、write方式,在用户空间和内核空间不断拷贝数据,占用了大量用户内存空间,效率不高。 
    内存映射方式:把设备里的内存映射到应用程序中的内存控件,直接处理设备内存,这是一种有效的方式。上面的mmap函数就是使用这种方式。 
    用户指针模式:内存片段由应用程序自己分配。这点需要在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR。 
  
  6)处理采集数据 
    V4L2有一个数据缓存,存放req.count数量的缓存数据。 
    数据缓存采用FIFO的方式,当应用程序调用缓存数据时,缓存队列将最先采集到的视频数据缓存送出,并重新采集一张视频数据。 
    这个过程需要用到两个ioctl命令,VIDIOC_DQBUF和VIDIOC_QBUF: 
     
    struct v4l2_buffer buf; 
    memset(&buf, 0, sizeof(buf)); 
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
    buf.memory = V4L2_MEMORY_MMAP; 
    buf.index = 0; 
    //读取缓存 
    if (ioctl(cameraFd, VIDIOC_DQBUF, &buf) == -1){ 
        return -1; 
    }

<think>好的,用户现在想解决在Armbian系统中使用v4l2-ctl命令配置视频设备/dev/video0,并设置NM12格式以及FFmpeg捕获H.264的问题。我需要回顾之前的对话,确保这次回答符合用户的需求,并补充可能遗漏的信息。 首先,用户之前遇到了无法识别usb0设备和FFmpeg捕获视频的问题。我之前提供了设备验证、格式支持检查、FFmpeg命令修正、权限配置和内核调试的步骤。现在用户特别提到了v4l2-ctl命令和NM12格式,可能需要更详细的格式配置指导。 用户提到的“NM12”格式可能是个笔误,常见的有MJPEG、YUYV、NV12等。考虑到NV12是常见的YUV格式,用户可能想设置摄像头输出NV12,但之前尝试的是H.264。需要确认摄像头是否支持NV12,或者是否需要通过v4l2-ctl强制设置格式。 接下来,我需要分步骤指导用户如何用v4l2-ctl列出设备、检查支持的格式,设置特定格式,并调整FFmpeg命令。同时要确保权限正确,并处理可能出现的错误,比如不支持H.264时如何转码。 另外,用户可能遇到的问题是,即使摄像头支持H.264,但FFmpeg无法直接捕获,需要正确参数。或者需要先设置摄像头输出NV12,再通过FFmpeg编码为H.264。需要区分这两种情况,给出不同的解决方案。 最后,生成的相关问题需要覆盖常见的使用场景和问题排查,比如如何设置默认格式、处理不支持的情况,以及检查硬件编码支持等。</think>### Armbian系统v4l2-ctl与H.264配置深度指南 #### 一、设备拓扑验证(关键步骤) ```bash v4l2-ctl --list-devices --all 2>&1 | grep -A 10 "video0" ``` 典型输出解析: ```text /dev/video0 # 主设备节点 Driver Info: uvcvideo # 使用UVC驱动 Bus info: usb-0000:01:00.0-1.2 # USB物理路径 Format Video Capture: # 当前捕获格式 Width/Height : 1920/1080 Pixel Format : 'H264' # 当前格式标记 ``` #### 二、NM12格式支持验证(注:应为NV12) 1. 查询设备能力: ```bash v4l2-ctl -d /dev/video0 --list-formats-ext | grep -E 'Index|Type|Pixel Format|Size' ``` 查找类似输出: ```text Pixel Format: 'NV12' (Y/CbCr 4:2:0) Size: Discrete 1280x720 ``` 2. 强制设置NV12格式: ```bash v4l2-ctl -d /dev/video0 --set-fmt-video=pixelformat=NV12,width=1280,height=720 ``` 验证设置结果: ```bash v4l2-ctl -d /dev/video0 --get-fmt-video ``` #### 三、H.264硬件流捕获方案 1. 直接捕获编码流: ```bash ffmpeg -f v4l2 -input_format h264 -video_size 1280x720 \ -i /dev/video0 -c copy output.h264 ``` *若出现`Unknown input format`错误,尝试:* ```bash ffmpeg -f v4l2 -video_size 1280x720 -i /dev/video0 -vcodec copy output.mp4 ``` 2. NV12转H.264方案(需CPU编码): ```bash v4l2-ctl -d /dev/video0 --set-fmt-video=pixelformat=NV12 ffmpeg -f v4l2 -input_format nv12 -video_size 1280x720 \ -i /dev/video0 -c:v libx264 -preset ultrafast output.mp4 ``` #### 四、高级调试技巧 1. 内核消息监控: ```bash dmesg -w | grep -iE 'uvc|video' ``` 插入/拔出摄像头时观察设备识别过程 2. UVC参数调优: ```bash # 查看当前参数 v4l2-ctl -d /dev/video0 --list-ctrls # 调整亮度参数示例 v4l2-ctl -d /dev/video0 --set-ctrl brightness=128 ``` #### 五、设备锁定配置(防止热插拔失效) ```bash # 创建udev规则 echo 'SUBSYSTEM=="video4linux", ATTR{index}=="0", SYMLINK+="camera_main"' | sudo tee /etc/udev/rules.d/99-camera.rules # 重载规则 sudo udevadm control --reload ``` --相关问题-- 1. 如何永久保存v4l2-ctl的视频格式设置? 2. FFmpeg提示"select timeout"错误如何解决? 3. 怎样检测摄像头是否支持硬件H.264编码? 4. NV12与YUY2格式在视频质量上有何区别?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值