目录
(1)video_manage.h、video_manage.c模块
数码相框部分有写好的代码,可以直接拷贝过来用。
新建工程文件,新建文件夹 render、 video、 convert、 include 等,主要实现功能如下:
video – 对设备的处理 :对 USB 摄像头进行操作,比如 open, close,十多个 ioctl
convert – 视频格式转换 :我们从 USB 摄像头中获取到的数据可能是不同格式的数据,而我们 LCD 只能支持 RGB 格式的数据
render – 缩放函数、合并函数
include – 头文件
Video 文件实现
实现 video 文件下的函数 (实现从摄像头读出数据)
我们在 video 文件下的框架是什么样的呢?
我们的 USB 摄像头硬件在 linux 内核中的驱动可能是 V4L2 框架,也可以是其他的框架比如 V4L,或者我们的 linux 内核中本来
并不支持我们这种摄像头驱动,是自己实现的某种摄像头的驱动,所以我们这个 video 目录下就要针对不同的摄像头驱动在应
用层就要有不同的对摄像头硬件的操作函数。所以我们 video 目录就要兼容不同的驱动来实现对他们的操作。
上图的框架理解:
我们要实现针对于 USB 摄像头不同底层驱动的操作函数,比如我们的摄像头在底层驱动中可能是用 V4L2 框架支持的,或者老
版的 V4L 框架支持的,那么我们就要分别实现一个 v4l2.c 和一个 v4l.c 来和底层对应。
在这两个文件里面要做的事情肯定就是调用驱动里面的 open, ioctl 等等函数来操作 USB 摄像头设备。
我们可以想到是构造一个操作 USB 摄像头设备的结构体: VideoOpr,这个结构体里面就是应用层来操作 USB 摄像头的各种函数
里面的成员肯定要有:
1: InitDevice:这个函数里面肯定就要调用 open 来打开 USB 摄像头设备,然后要调用 ioctl,比如查询我们摄像头是什么类
型的设备(视频捕获还是其他),查询支持什么格式的数据等等
2: ExitDevice:和 InitDevice 对应
3: GetFrame:通过 Poll 来从缓冲区里获取视频的数据,然后调用 VIDIOC_DQBUF ioctl 从队列中删除这个 buffer
4: PutFrame: 调用 VIDIOC_QBUF 将一个缓冲区放入队列中
5: StartDevice:调用 VIDIOC_STREAMON ioctl 启动数据传输
6: StopDevice:调用 VIDIOC_STREAMOFF 关闭数据传输
v4l2.c 和 v4l.c 就是要实现上面的结构体中的几个函数,之后注册给上层统一的接口
那么上层的文件我们同样在 video 中实现,也就是 video_manager.c.这个文件我们知道其实就是实现一个 VideoOpr 链表,将
前面的 v4l2.c 和 v4l.c 实现的 VideoOpr 结构体通过链表管理起来
另外,我们上面要实现的 VideoOpr 结构体的函数参数我们应该传递什么呢?我们通过 VideoOpr 中函数无非就是对 USB 摄像头
设备进行操作,所以我们要定一个描述 USB 摄像头设备的结构体,通过各种 ioctl 命令之后就可以获取到这个 USB 摄像头的信
息:
VideoDevice:
他里面的成员:
1: iFd:文件句柄,我们在 VideoOpr 中的 InitDevice 的肯定要进行 open 操作, open 出来的某个文件的文件描述符我们要记
录下来
2: iPixelFormat:我们视频数据的格式,我们在调用 VIDIOC_ENUM_FMT 是可以获得该 USB 视频设备支持的数据格式
3: iWidth:我们在 VIDIOC_S_FMT 或者 VIDIOC_G_FMT 肯定要涉及分辨率的信息
4: iHeight:同 iWidth
5: iVideoBufCnt:我们分配的缓冲区的个数
6: iVideoBufMaxLen:缓冲区的大小
7: iVideoBufCurIndex:当前是正在使用哪个缓冲区,也就是说当前是哪个缓冲区有数据
8: unsigned char *pucVideBuf[]:我们应用层对应的所有缓冲区,我们应用层申请缓冲区之后, mmap 将内核的缓冲区映射到
了应用层,也就是说我们这个 unsigned char *pucVideBuf 就是相当于内核对应的缓冲区了
9: ptOPr:这个就是我们 VideoOpr 这个结构体变量,之所以放这里面,是为了方便
另外我们的 VideoOpr 的操作函数中涉及到了视频的数据操作,所以我们要定义一个 VideoBuf 结构用来描述一个缓冲区的数据
的信息:比如某个缓冲区有数据了,那么这帧数据的所代表的分辨率是多少,他的实际的数据内容又是什么呢?
我们可以用之前的 PixelDatas 来表示具体这帧数据代表的含义:
另外我们还需要知道这个数据的数据格式是什么,所以最终我们的 VideoBuf 定义如下:
主要 video 涉及到结构体就上面提及到的几个,我们把这几个结构体放到一个 video_manager.h 中
摄像头模块即video部分代码,这部分代码实现对设备的处理,具体做法是从摄像头中将视频数据读出供后续模块处理
(1)video_manage.h、video_manage.c模块
① video_manage.h:负责抽象出所有与设备有关的结构体
#ifndef _VIDEO_MANAGER_H
#define _VIDEO_MANAGER_H
#include <config.h>
#include <pic_operation.h>
#include <linux/videodev2.h>
#define NB_BUFFER 4
/* 这里有一个问题:在VideoDevice 中引用了VideoOpr 结构体,同时在VideoOpr 结构体中的函数又引用了VideoDevice 结构体,这里交叉引用了。因此需要在最前面首先声明,后面就可以引用了 */
struct VideoDevice;
struct VideoOpr;
typedef struct VideoDevice T_VideoDevice, *PT_VideoDevice;
typedef struct VideoOpr T_VideoOpr, *PT_VideoOpr; //PT_VideoOpr为指向结构体的指针类型
struct VideoDevice { //用该结构体表示这个设备
int iFd; //记录打开设备时的文件句柄
int iPixelFormat; //摄像头视频数据的格式
int iWidth; //分辨率的宽
int iHeight; //分辨率的高
int iVideoBufCnt;
int iVideoBufMaxLen;
int iVideoBufCurIndex;
unsigned char *pucVideBuf[NB_BUFFER]; //用来存放mmap之后的地址。
/* 函数 */
PT_VideoOpr ptOPr; //ptOPr指向VideoOpr 这个结构体
};
注释:当我们在程序中构造VideoDevice 实体时,就会让ptOPr这个结构体指向我们在v4l2.c文件中构造的VideoOpr 结构体
/* v4l2.c */
/* 构造一个VideoOpr结构体 */
static T_VideoOpr g_tV4l2VideoOpr = {
.name = "v4l2",
.InitDevice = V4l2InitDevice,
.Exit