Linux网络摄像头设备端调试过程

转载请标明出处:https://blog.csdn.net/u013752202/article/details/96502576

简介

        网络摄像头,即常说的免驱摄像头,其采用UVC协议通过USB跟主机通信,因其不需要编写单独的驱动,方便使用而得到广泛使用。

        主机端无论是windows还是linux使用UVC摄像头都非常简单,也有很多文章详细介绍使用方法。但如何使自己的设备变成一个网络摄像头,却鲜有文章提及,本文重点分析如何构建一台网络摄像头。

 

硬件和软件基础

        硬件:一套带有MIPI/DVP摄像头模组和USB接口的嵌入式主板

        软件:系统linux,MIPI/DVP驱动是通的(本文不做介绍)

 

系统结构

        网络摄像头涉及到两套linux驱动框架,V4L2视频采集框架,USB驱动框架。两套框架在linux系统中已经非常成熟,我们只需要会配置和应用就可以搭建出一个网络摄像头了。网络摄像头的系统结构如图1所示。

                                                   图1 网络摄像头系统结构

驱动配置

         网络摄像头系统结构分为两部分,即驱动和应用。

         驱动又分为视频采集驱动和视频输出驱动,如果只想从文件中读取视频然后通过USB传输到电脑上观看的话,那么视频采集驱动不是必需的。

         视频采集驱动的功能是通过i2c配置CMOS模组的寄存器并采集图像数据,如果有需要编码的话,还负责编码数据。该驱动需要根据CMOS寄存器实现一个v4l2的子设备即可。具体目录为:drivers/media/i2c/soc_camera

         视频输出驱动配置

         在linux中已经实现了webcam的虚拟摄像头驱动,只要配置打开即可。这里有两种配置方法,linux原生驱动和Android usb驱动。这里先介绍linux原生驱动。

                                         图2 linux原生webcam驱动配置

         执行make menuconfig,按照图2 webcam配置即可。配置完成后编译kernel。源码目录位于:drivers/usb/gadget

应用编写

         根据图1结构图,网络摄像头需要一个应用程序uvc_gadget来从视频输入设备或者文件中获取视频流,然后通过视频输出设备向电脑或其他USB主机输出视频。

         视频输入设备的打开、配置和读取跟平常一样操作,分为Open->format setting->request buffer(mmap) ->stream on->read frame->stream off。设备缓存类型配置为:V4L2_BUF_TYPE_VIDEO_CAPTURE

         视频输出设备会复杂一点,需要根据UVC建立连接的过程来配置和打开设备,UVC的事件包括:CONNECT/DISCONNECT/SETUP/DATA/STREAMON/STREAMOFF。

其中虚拟摄像头的配置需要在STREAMON的时候配置,STREAMOFF的时候关闭,否则只能打开一次摄像头,重新打开只能重新插拔USB了。设备缓存类型配置为:V4L2_BUF_TYPE_VIDEO_OUTPUT

         可以用select()监控视频输出设备的事件状态和写状态,当可写时候,即可把视频输入设备读取到的视频流填充到输出设备的缓存中,这样电脑上就能看到画面了。

网络摄像头源码:待发布……(可与作者联系)

转载请标明出处:https://blog.csdn.net/u013752202/article/details/96502576

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
Linux下开发mipi摄像头,需要用到V4L2框架,以下是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #include <linux/videodev2.h> #define CAM_DEV "/dev/video0" #define WIDTH 640 #define HEIGHT 480 int main() { int fd; int ret; struct v4l2_capability cap; struct v4l2_format fmt; struct v4l2_requestbuffers reqbuf; struct v4l2_buffer buf; fd = open(CAM_DEV, O_RDWR); if (fd < 0) { printf("open %s failed\n", CAM_DEV); return -1; } ret = ioctl(fd, VIDIOC_QUERYCAP, &cap); if (ret < 0) { printf("VIDIOC_QUERYCAP failed\n"); close(fd); return -1; } memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = WIDTH; fmt.fmt.pix.height = HEIGHT; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; ret = ioctl(fd, VIDIOC_S_FMT, &fmt); if (ret < 0) { printf("VIDIOC_S_FMT failed\n"); close(fd); return -1; } memset(&reqbuf, 0, sizeof(reqbuf)); reqbuf.count = 4; reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; reqbuf.memory = V4L2_MEMORY_MMAP; ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuf); if (ret < 0) { printf("VIDIOC_REQBUFS failed\n"); close(fd); return -1; } for (int i = 0; i < reqbuf.count; i++) { memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; ret = ioctl(fd, VIDIOC_QUERYBUF, &buf); if (ret < 0) { printf("VIDIOC_QUERYBUF failed\n"); close(fd); return -1; } void *addr = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); if (addr == MAP_FAILED) { printf("mmap failed\n"); close(fd); return -1; } buf.m.userptr = (unsigned long)addr; ret = ioctl(fd, VIDIOC_QBUF, &buf); if (ret < 0) { printf("VIDIOC_QBUF failed\n"); close(fd); return -1; } } enum v4l2_buf_type type; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(fd, VIDIOC_STREAMON, &type); if (ret < 0) { printf("VIDIOC_STREAMON failed\n"); close(fd); return -1; } while (1) { memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; ret = ioctl(fd, VIDIOC_DQBUF, &buf); if (ret < 0) { printf("VIDIOC_DQBUF failed\n"); close(fd); return -1; } // 这里可以对buf.m.userptr指向的数据进行处理或保存 ret = ioctl(fd, VIDIOC_QBUF, &buf); if (ret < 0) { printf("VIDIOC_QBUF failed\n"); close(fd); return -1; } } return 0; } ``` 这个示例代码中,首先打开摄像头设备文件(CAM_DEV),然后调用VIDIOC_QUERYCAP查询驱动功能,调用VIDIOC_S_FMT设置摄像头采集格式,调用VIDIOC_REQBUFS请求内存,调用mmap将内存映射到用户空间,调用VIDIOC_QBUF将内存缓冲区入队列,最后调用VIDIOC_STREAMON开始采集。然后进入循环,每次采集完成后,调用VIDIOC_DQBUF将内存缓冲区出队列,进行处理或保存,然后调用VIDIOC_QBUF将内存缓冲区再次入队列,继续采集。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叶落西湘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值