linux下通过V4L2驱动USB摄像头

linux下通过V4L2驱动USB摄像头目录目录前言 v4l2 解析 v4l2 介绍应用程序通过 V4L2 接口采集视频数据步骤相关结构体解析参考链接前言在移植罗技 C270 摄像头到 6818 的过程中 内核已经检测到了 USB 摄像头 但是直接用 OpenCV 的 API 比如 CvCapture cvCaptureFro intindex 接口 无法打开 USB 摄像头 至少目前我是这么认为的 然后 网上搜索答案

目录

前言

在移植罗技C270摄像头到6818的过程中,内核已经检测到了USB摄像头,但是直接用OpenCV的API(比如CvCapture*cvCaptureFromCAM(int index)接口,无法打开USB摄像头,至少目前我是这么认为的。然后,网上搜索答案说是要使用V4l2进行操作。没有别的办法!只有一边学一边试试看行不行喽!

感谢超群天晴大神的(原创)基于ZedBoard的Webcam设计(一):USB摄像头(V4L2接口)的图片采集这篇博文中提供的源码,我直接移植后,在6818是成功获取bmp图片和yuv图片。虽然在Ubuntu中打开bmp图片和yuv图片是错误的!!!

在找到超群天晴的博文后,我又找到这两篇博客和菜鸟一起学linux之V4L2摄像头应用流程和嵌入式LINUX环境下视频采集知识验证成功后,就是花时间理解,然后获取视频流了!希望获取的视频流能够流畅!

学习!分享!感谢!

v4l2解析

v4l2介绍

v4l2是linux操作系统下用于采集图片、视频和音频数据的API接口,配合适当的视频采集设备和相应的驱动程序,可以实现图片、视频、音频等的采集。在远程会议、可视电话、视频监控系统和嵌入式多媒体终端中都有广泛应用。
在linux下,所有外设都被看成一种特殊的文件,称为”设备文件”,可以像访问普通文件一样对设备文件进行访问。
V4L2支持两种方式来采集图像:内存映射(mmap)和直接读取方式(read)。V4L2在include/linux/video.h文件下定义了一些重要的数据结构,在采集图像的过程中,就是通过对这些数据的操作来获得最终的图像数据。Linux系统V4L2使能可在内核编译阶段配置,默认情况下是在make menuconfig是打开的。




应用程序可以通过V4L2进行视频采集。V4L2支持内存映射(mmap)方式和直接读取方式(read)方式采集数据。前者一般用于连续的视频数据采集,后者常用静态图片数据采集。

v4l2 中不仅定义了通用API元素,图像的格式,输入/输出方法,还定义了Linux内核驱动处理视频信息的一系列接口,这些接口主要有:

视频采集接口——Video Capture interface 视频输出接口 ——Video Output Interface; 视频覆盖/预览接口——Video Overlay Interface; 视频输出覆盖接口—— Video Output Overlay Interface; 编解码接口——Codec Interface

应用程序通过V4L2接口采集视频数据步骤

  • 打开视频设备文件,通过视频采集的参数初始化,通过V4L2接口设置视频图像属性。
  • 申请若干视频采集的帧缓存区,并将这些帧缓冲区从内核空间映射到用户空间,便于应用程序读取/处理视频数据。
  • 将申请到的帧缓冲区在视频采集输入队列排队,并启动视频采集。
  • 驱动开始视频数据的采集,应用程序从视频采集输出队列中取出帧缓冲区,处理后,将帧缓冲区重新放入视频采集输入队列,循环往复采集连续的视频数据。
  • 停止视频采集。
    具体实现过程如下图:
    2




  • 整理下其中ioctl控制符:
VIDIOC_QUERYCAP 查询设备的属性 VIDIOC_ENUM_FMT 帧格式 VIDIOC_S_FMT 设置视频帧格式,对应struct v4l2_format VIDIOC_G_FMT 获取视频帧格式等 VIDIOC_REQBUFS 请求/申请若干个帧缓冲区,一般为不少于3个 VIDIOC_QUERYBUF 查询帧缓冲区在内核空间的长度和偏移量 VIDIOC_QBUF 将申请到的帧缓冲区全部放入视频采集输出队列 VIDIOC_STREAMON 开始视频流数据的采集 VIDIOC_DQBUF 应用程序从视频采集输出队列中取出已含有采集数据的帧缓冲区 VIDIOC_STREAMOFF 应用程序将该帧缓冲区重新挂入输入队列 
V4L2_BUF_FLAG_UNMAPPED 0B0000 V4L2_BUF_FLAG_MAPPED 0B0001 V4L2_BUF_FLAG_ENQUEUED 0B0010 V4L2_BUF_FLAG_DONE 0B0100 

相关结构体解析

  1. VIDIOC_QUERYCAP——–>struct v4l2_capability
struct v4l2_capability {     __u8 driver[16]; // 驱动模块的名字 __u8 card[32]; // 设备名字 __u8 bus_info[32]; // 总线信息 __u32 version; // 内核版本 __u32 capabilities; // 整个物理设备支持的功能 __u32 device_caps; // 通过这个特定设备访问的功能 __u32 reserved[3]; }; 

如下是我的罗技C270摄像头的通过VIDIOC_QUERYCAP获取的设备功能

driver: uvcvideo card: UVC Camera (046d:0825) bus_info: usb-nxp-ehci-1.3 version:  capabilities:  Device /dev/video9: supports capture. Device /dev/video9: supports streaming. 

其中capabilities: 通过与各种宏位与,可以获得物理设备的功能属性。比如:

 //#define V4L2_CAP_VIDEO_CAPTURE 0x00000001 /* Is a video capture device */ // 是否支持视频捕获 if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE) { 
    printf("Device %s: supports capture.\n", FILE_VIDEO); } //#define V4L2_CAP_STREAMING 0x0 /* streaming I/O ioctls */ // 是否支持输入输出流控制 if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING) { 
    printf("Device %s: supports streaming.\n", FILE_VIDEO); } 
  1. VIDIOC_ENUM_FMT——–>struct v4l2_fmtdesc
/* * F O R M A T E N U M E R A T I O N */ struct v4l2_fmtdesc { 
    __u32 index; /* Format number */ __u32 type; /* enum v4l2_buf_type */ __u32 flags; __u8 description[32]; /* Description string */ __u32 pixelformat; /* Format fourcc */ __u32 reserved[4]; }; 

通过这个结构体,可以显示对应的摄像头所支持视频帧格式。

struct v4l2_fmtdesc fmtdesc; fmtdesc.index = 0; fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; printf("Supportformat:/n"); while(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1) { 
    printf("/t%d.%s/n",fmtdesc.index+1,fmtdesc.description); fmtdesc.index++; } 

我的摄像头输出如下:

Support format: 1.YUV 4:2:2 (YUYV) 2.MJPEG 

所以,我要读取视频帧的时候就要使用YUV422这种格式。
3. VIDIOC_S_FMT&VIDIOC_G_FMT——–>struct v4l2_format
查看或设置视频帧格式




struct v4l2_format { __u32 type; // 帧类型 union { /* V4L2_BUF_TYPE_VIDEO_CAPTURE */ struct v4l2_pix_format pix; //像素格式 /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */ struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */ struct v4l2_window win; /* V4L2_BUF_TYPE_VBI_CAPTURE */ struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */ struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SDR_CAPTURE */ struct v4l2_sdr_format sdr; /* user-defined */ __u8 raw_data[200]; } fmt; }; 
/* * V I D E O I M A G E F O R M A T */ struct v4l2_pix_format { __u32 width; // 像素高度 __u32 height; // 像素宽度 __u32 pixelformat; // 像素格式 __u32 field; /* enum v4l2_field */ __u32 bytesperline; /* for padding, zero if unused */ __u32 sizeimage; __u32 colorspace; /* enum v4l2_colorspace */ __u32 priv; /* private data, depends on pixelformat */ __u32 flags; /* format flags (V4L2_PIX_FMT_FLAG_*) */ __u32 ycbcr_enc; /* enum v4l2_ycbcr_encoding */ __u32 quantization; /* enum v4l2_quantization */ __u32 xfer_func; /* enum v4l2_xfer_func */ }; 
struct v4l2_streamparm { __u32 type; /* enum v4l2_buf_type */ union { struct v4l2_captureparm capture; struct v4l2_outputparm output; __u8 raw_data[200]; /* user-defined */ } parm; }; struct v4l2_captureparm { /* Supported modes */ __u32 capability; /* Current mode */ __u32 capturemode; /* Time per frame in seconds */ struct v4l2_fract timeperframe; /* Driver-specific extensions */ __u32 extendedmode; /* # of buffers for read */ __u32 readbuffers; __u32 reserved[4]; }; // timeperframe // numerator和denominator可描述为每numerator秒有denominator帧 struct v4l2_fract { __u32 numerator; // 分子 __u32 denominator; // 分母 }; 
  1. VIDIOC_REQBUFS——–>struct v4l2_requestbuffers
    申请和管理缓冲区,应用程序和设备有三种交换数据方法,直接read/write(裸机)、内存映射(系统),用户指针(…)。一般在操作系统管理下,都是使用内存映射的方式。

/* * M E M O R Y - M A P P I N G B U F F E R S */ struct v4l2_requestbuffers { 
    __u32 count; // 缓冲区内缓冲帧的数目 __u32 type; // 缓冲帧数据格式 __u32 memory; //  __u32 reserved[2]; }; enum v4l2_memory { 
    V4L2_MEMORY_MMAP = 1, // 内存映射 V4L2_MEMORY_USERPTR = 2, // 用户指针 V4L2_MEMORY_OVERLAY = 3, V4L2_MEMORY_DMABUF = 4, }; 
  1. VIDIOC_QUERYBUF——–>struct v4l2_buffer
struct v4l2_buffer { 
    __u32 index; // buffer的id __u32 type; // enum v4l2_buf_type __u32 bytesused; // buf中已经使用的字节数 __u32 flags; // MMAP 或 USERPTR __u32 field; struct timeval timestamp; // 帧时间戳 struct v4l2_timecode timecode; __u32 sequence; // 队列中的序号 /* memory location */ __u32 memory; union { 
    __u32 offset; // 设备内存起始offset unsigned long userptr; // 指向用户空间的指针 struct v4l2_plane *planes; __s32 fd; } m; __u32 length; // 缓存帧长度 __u32 reserved2; __u32 reserved; }; 
  1. VIDIOC_DQBUF
    应用程序从视频采集输出队列中取出已含有采集数据的帧缓冲区

  2. VIDIOC_STREAMON&VIDIOC_STREAMOFF
    开始视频采集和关闭视频采集

  3. VIDIOC_QBUF
    应用程序将该帧缓冲区重新挂入输入队列

总结

使用C语言高级应用—操作linux下V4L2摄像头应用程序源码成功的在我的开发板上显示出了800600的视频图像。我的开发板的显示屏是1024600的,当我设置为1024600时,实际显示为1024576,感觉很奇怪!而且显示的视频是反的,都是小问题。总之,显示视频的那一刻真的好开心!

参考链接

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/204565.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午8:02
下一篇 2026年3月19日 下午8:03


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号