您的位置:首页 > 其它

[转]OV511视频采集设计文档

2011-01-07 15:49 344 查看
关键词:
OV511 ARM Linux s3c2440

BCNG


OV511视频采集设计文档


本:1.0



者:hgang



期:2008-07-12





一. 概述...

二. 编译内核与驱动加载...

1. 静态加载...

2. 动态加载...

三. 设计方案...

3. 定义的数据结构...

4. 视频编程流程及函数实现...

四. 调试过程...

五. 存在的问题和拟采取的解决方案...

一.
概述

本项目是在基于S3C2440的嵌入式开发板上实现视频图像采集,压缩,传输等功能,本文主要介绍视频采集部分。

视频采集使用的是带有OV511芯片的V2000摄像头,linux系统自带了OV511的驱动程序,所以无需另外安装驱动,只需在编译内核时加载进去

二.
编译内核与驱动加载

1. 静态加载

(1) 在/home/xiyong/bcng2440/linux-bcng2440-xiyong目录下make menuconfig。

(即板子的内核目录,如果要在PC上使用摄像头则在/usr/src/linux-2.4下操作)

(2) 首先(*)(“y”键)选择Multimedia device->下的Video for linux。加载video4linux模块,为视频采集设备提供编程接口;

(3) 然后在usb support->目录下(*)选择support for usb和usb camera ov511
support。这使得在内核中加入了对采用OV511接口芯片的USB数字摄像头的驱动支持。

(4) 保存配置退出。

(5)
make dep;

(6) 修改/home/xiyong/bcng2440/linux-bcng2440-xiyong/drivers/media/video目录下

videodev.c程序(修改方法在后面调试过程部分有叙述)。

(7)
make
zImage,然后cp ./arch/arm/boot/zImage /tftpboot/;

2. 动态加载

(1) 在arm linux的kernel目录下make menuconfig。

(2) 首先<*>选择Multimedia device->下的Video for linux。

(3) 然后在usb support->目录下<*>选择support for usb和<M>选择usb camera
ov511 support。

(4) 保存退出。

(5)
Make
dep;

(6) 修改videodev.c程序。

(7)
make
zImage;make modules然后就在/driver/usb下生成ov511.o,同
时生成的zImage自动放在/tftpboot下。

(8) 然后用新内核启动板子后insmod ov511.o就可以成功加载。

我们使用的是静态加载;内核编译完以后板子的启动命令为t 30008000 zImage; go 30008000;

需要注意的是:

(1) 编译内核是要选择正确的路径,编译PC机内核路径是/usr/src/linux-2.4.18-14,编译板子是/home/xiyong/bcng2440/linux-bcng2440-xiyong。

(2) 注意“*”(y键)选和“M”选的不同。

三.
设计方案

3. 定义的数据结构

typedef struct v4l_struct

{

int
fd;//设备名

struct
video_capability capability;//设备信息

struct
video_picture picture;//摄像头缓冲区内图像信息

struct
video_window window;//窗口信息

struct
video_mmap mmap;//用于内存映射

struct
video_mbuf mbuf;//利用mmap进行映射的帧的信息

unsigned char *map;//用于存储映射得到的图片的首地址

int
framestat[2];//定义帧状态

}v4l_device;

(1)
video_capability
包含设备的基本信息(设备名称、支持的最大最小分辨率、信号源信息等)
name[32]
设备名称
maxwidth
maxheight
minwidth
minheight
Channels
信号源个数
type
是否能capture

彩色还是黑白,
是否能裁剪等等。值如VID_TYPE_CAPTURE等

可以调用

ioctl(vd->fd,VIDIOCGCAP,&(vd->capability)

来读取这些信息。

(2)
video_picture
设备采集的图象的各种属性
Brightness
0~65535
hue
colour
contrast
whiteness
depth 8 16 24 32
palette
VIDEO_PALETTE_RGB24 | VIDEO_PALETTE_RGB565|
VIDEO_PALETTE_JPEG|
VIDEO_PALETTE_RGB32|VIDEO_PALETTE_YUV420等。

可以调用

ioctl(vd->fd,VIDIOCGPICT,&(vd->picture))读取各信息,

ioctl(vd->fd,VIDIOCSPICT,&(vd->picture))重新设置各信息分量。

(3)
video_mbuf
利用mmap进行映射的帧的信息size
每帧大小
Frames
最多支持的帧数
Offsets
每帧相对基址的偏移

可以调用

ioctl(vd->fd, VIDIOCGMBUF,
&vd->mbuf)

读取各信息分量。

这部分出现的问题:

(1)
在程序开始之时声明一个v4l_device结构之后一定要为其分配内存空间

v4l_device *vd;

vd=(v4l_device *)malloc(sizeof(v4l_device));

否则会导致问题,在读取vd->mbuf信息时vd->mbuf.offsets为一个非常大的负值,然后在读取映射图片的地址时(vd->map
+ vd->mbuf.offsets[frame])就会导致读取数据失败。

4. 视频编程流程及函数实现

(1)打开视频设备和输出文件:

char *devicename="/dev/video0";

if((vd->fd = open(devicename,O_RDWR))<0)

{

perror("v4l_open:");

return -1;

}//打开设备。

(2) 读取设备及图片信息

int
v4l_get_capability(v4l_device *vd)

{

if(ioctl(vd->fd,VIDIOCGCAP,&(vd->capability))<0)

{

perror("v4l_get_capability:");

return -1;

}

if(ioctl(vd->fd, VIDIOCGWIN, &vd->window) != 0)

{

perror("ioctl (VIDIOCGWIN)");

return -1;

}

return
0;

}

int v4l_get_picture(v4l_device
*vd)

{

if(ioctl(vd->fd,VIDIOCGPICT,&(vd->picture))<0)

{

perror("v4l_get_picture:");

return -1;

}

(3)更改当前设置

int v4l_grab_init(v4l_device
*vd)

{

vd->mmap.width=320;

vd->mmap.height=240;

vd->mmap.format=VIDEO_PALETTE_YUV420;

printf("vd->mmap.format%d/n",vd->mmap.format);

vd->mmap.frame=0;

vd->framestat[0]=0;

vd->framestat[1]=0;

vd->picture.brightness=19968;

vd->picture.hue=32768;

vd->picture.colour=65535;

vd->picture.contrast=22016;

vd->picture.whiteness=26880;

vd->picture.palette=vd->mmap.format;

ioctl(vd->fd,VIDIOCSPICT,&(vd->picture));

//ioctl(vd->fd,VIDIOCGPICT,&(vd->picture));

return 0;

}

这一步非常重要,如不注意会直接导致采集失败,应注意两点,一是一定要对vd->picture.palette进行设置,且与vd->mmap.format一致,这两处的功能是设置采集到的图像格式,可根据需要格式进行设置。二是调用ioctl(vd->fd,VIDIOCSPICT,&(vd->picture))设置之后切不可再次调用ioctl(vd->fd,VIDIOCGPICT,&(vd->picture)),否则会再次恢复默认值。另外picture其他分量会影响图像的效果,可调整其值以达到最佳效果。

(4)进行视频采集(使用内存映射方法)

int v4l_mmap_init(v4l_device
*vd)

{

if(ioctl(vd->fd, VIDIOCGMBUF, &vd->mbuf) ==
0)

{

printf("video_mbuf:/nsize:%d/nframes:%d/noffsets:%d/n",vd->mbuf.size,

vd->mbuf.frames,vd->mbuf.offsets);

if((vd->map=mmap(0,vd->mbuf.size,PROT_READ|PROT_WRITE,MAP_SHARED,vd->fd,0))<0)

/*将mmap与video_mbuf绑定。*/

{

perror("v4l_mmap_init:mmap");

return -1;

}

}

内存映射;关键函数void *mmap( void *addr, size_t len, int prot, int flags,
int fd, off_t offset),返回值是系统实际分配的起始地址;

各参数含义为:

len:映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起;

prot:制定共享内存的访问权限 PROT_READ(可读), PROT_WRITE(可写), PROT_EXEC(可执行);

Flags:MAP_SHARED , MAP_PRIVATE中必选一个,一般为前者;

Addr:共享内存的起始地址,一般设为0,表示由系统分配

int v4l_grab_start(v4l_device *vd,int
frame)

{

vd->mmap.frame = frame;

if((ioctl(vd->fd,VIDIOCMCAPTURE,&(vd->mmap)))<0)

{

perror("v4l_grab_start:");

return -1;

}

vd->framestat[frame] = 1;

printf("grab_start successful/n");

return
0;

}

开始一帧的映射;

int v4l_grab_sync(v4l_device *vd,int
frame)

{

if(ioctl(vd->fd,VIDIOCSYNC,&(vd->mmap.frame))<0)

{

perror("v4l_grab_sync:");

return -1;

}

vd->framestat[frame] = 0;

//
printf("grab_sync successful/n");

return
0;

}

等待一帧映射结束;

unsigned char *v4l_getaddress(v4l_device *vd,int frame)

{

return
(vd->map + vd->mbuf.offsets[frame]);

}

获取帧的地址;

(5)对采集的视频进行处理

if((outfile=open("/tmp/temp.YUV",O_WRONLY |
O_CREAT))<0)

{

perror("open outfile error");

return -1;

}

size=(vd->mmap.width * vd->mmap.height *
3)/2;

if(write(outfile,buffer,size)<0)//将内存中的图片信息写入文件中。

{

printf("write outfile error");

return -1;

}

需要注意的是:

1. 打开输出文件的目录因该是板子上的目录,而不是PC机上的,因为程序运行是在板子上运行,所以它寻找路径是从板子上的根目录开始,而与pc机上的目录无关。

2. 对于YUV420格式,size的大小一定要正确,因为此格式图像各分量是分块排列,size错误会导致图像数据整个错误。

(6)关闭视频设备。

close(outfile);

munmap(vd->map,vd->mbuf.size);//取消映射,对应mmap。

close(vd->fd);

此处取消映射一定要在写文件之后,否则会导致数据丢失而无法写入。

单帧采集时只需设置frame=0;同时无需定义帧状态(vd->framestat[frame]),vd->map直接得到帧数据起始地址;而连续帧采集时,由读取的vd->mbuf.frames=2可以知道内存映射时最多支持两帧,定义int vd->framestat[2]表示帧状态,用frame=0 or1表示当前为那一帧,通过改变vd->framestat和frame的值来进行双缓冲,即采集一帧时处理另一帧,同时加一个外循环达到连续多帧的采集,每帧的地址都是通过vd->map +
vd->mbuf.offsets[frame]得到,值得注意的是,由于偏移地址只有vd->mbuf.offsets[0] 和 vd->mbuf.offsets[1],
在采集完下一帧之前必须将当前帧处理完毕,否则再次采集到的新数据会将当前数据覆盖。

四.
调试过程

1. 打开设备出错,

提示信息:

not such device or directory.

原因:没有建立文件节点,可先用cat/proc/devices查看到video caprure device的主设备号是81,再ls –l /dev看到video0次设备号为0.然后mknod /dev/video 0 c 81 0;建立设备节点。

Open v4l::No such devices

Mknod后用cat查看还是没有81设备号;

原因:编译内核时/home/xiyong/bcng2440/linux-bcng2440-xiyong/drivers/media/video目录下

videodev.c程序没有改动,

以前的代码是通过MODULE的方式运行的,

#ifdef
MODULE

int
init_module(void)

{

return
videodev_init();

}

现在修改为

module_init(videodev_init);

module_exit(videodev_exit);

但是发现还是没有运行,又发现需要在函数前增加_init的标记才能运行

static int
__init

videodev_init(void)

现在能够运行了,在启动信息中可以发现如下的信息:

Video for Linux One (2.2.16). Major device:
81

Video for Linux Two (V0.20). Major device:
81

使用cat /proc/devices可以看到设备名字:81
v4l1/2

2. 读取设备信息出错,

提示信息:

VIDIOCMCAPTURE: invalid format
(Unknown)

原因:没有对vd->mmap各参数进行设置,只需对其width,height,format等设置相应值就行了。

3. 打开输出文件出错,

提示信息:

open outfile error: No such file or
directory

原因如前面所述把电脑上的目录和板子的目录搞混了,修改文件目录为板子目录就行了。

4. 写入文件失败,没有数据可写,

原因:mummap()放到了write之前,交换位置即可。

5. 程序能正常运行了,但读取的数据错误,读取的各设备分量信息中发现不合理值

vd->mbuf.offsets:-1073742308,

原因:没有给v4l_device *vd分配内存空间,程序开始时分配即可。

6. 采集到的数据不能显示;

原因:关键是没有设置vd->picture.palette,将其设置为与vd->mmap.format相同值即可。如果设置的是YUV420格式,则数据可以用YUVviewer播放,如果数据格式设为RGB24则还需加上BMP头后数据才能显示为图像。

其中RGB24图像格式为:每个像素点包含R,G,B三个分量,且三分量交替排列,即(R,G,B);(R,G,B);(R,G,B);……这样交替出现,如果不加BMP头图像大小就为width*height*3.

YUV格式又分为很多种,各种格式的Y,U,V分量的比例以及排列顺序不一样,比如UYUV就是[u,y,v,y][u,y,v,y]交替排列,比例为Y:U:V=4:2:2,图像大小就为width*height*2.

而yuv420则不一样,首先是比例,每行都只有U分量或者V分量,Y:U:V=4:2:0或4:0:2;

其次排列形式也不一样,YUV420三分量不是交替排列,而是各自分块排列,所有y分量之后才是u分量,最后是v分量。

如一幅4*4的图片其像素排列为:

y y y
y

y y y
y

y y y
y

y y y
y

u
u

u
u

v
v

v
v

图片大小为width*height*1.5.

7. 可以成功采集到连续的视频了,但是存在的对连续视频的帧采集速度太慢,加入时

间函数测了一下,每秒只能采集一到两帧,且主要是将数据写入文件部分耗时太多,

原因:写入文件的目录为“/temp.YUV”,这个目录还是在PC机上,写入时还是要通过网线传到电脑上,将目录改为“/tmp/temp.YUV”就可以大为缩短时间,“/tmp”目录是在板子上的本地目录,这可以在PC上打开/home/nfs/tmp和在板子上打开/tmp查看内容发现内容不同来验证。

xiyong8260的补充:

2008-5-29

1、编译内核,使用make menuconfig加上ov511(在usb选项中)的和vedio for linux驱动,

编译的时候错误,报告videodev.h文件不能找到version.h文件,后来修改/usr/local/arm/….
videodev.h文件,删除包含version.h,就好了。

2、启动内核,会打印下面的信息:

hub.c: new USB
device usb-ohci-1, assigned address 2

ov511.c: USB
OV511+ video device found

ov511.c: model:
Unknown

ov511.c: Camera
type (108) not recognized

ov511.c: Please
notify mark@alpha.dyndns.org of the name,

ov511.c:
manufacturer, model, and this number of your camera.

ov511.c: Also
include the output of the detection process.

ov511.c: Sensor
is an OV7620

Looking up
port of
RPC 100005/1
on 192.168.10.135

ov511.c: Device
registered on minor 0

说明检测到了OV511的设备,但是model不认识,查看源代码,camlist中不包含108的设备号;

3、编写了一个测试程序,打开/dev/video0设备

建立设备号 /dev/videio0 c 81 0,测试结果不能打开设备,怀疑是没有这个设备,

查看源代码,发现是driver/meida/video/videodev.c文件处理的video0设备,

进一步发现,没有运行videodev_init程序,

以前的代码是通过MODULE的方式运行的,

#ifdef
MODULE

int
init_module(void)

{

return
videodev_init();

}

现在修改为

module_init(videodev_init);

module_exit(videodev_exit);

但是发现还是没有运行,又发现需要在函数前增加_init的标记才能运行

static int
__init

videodev_init(void)

现在能够运行了,在启动信息中可以发现如下的信息:

Video for Linux
One (2.2.16). Major device: 81

Video for Linux
Two (V0.20). Major device: 81

使用cat /proc/devices可以看到设备名字:81 v4l1/2

[root@192 /]# cat
/proc/devices

Character
devices:

1 mem

2 pty/m%d

3 pty/s%d

4 vc/0

5 ptmx

7 vcs

10 misc

13 input

29 fb

81 v4l1/2

89 i2c

90 mtd

128
ptm

136
pts/%d

162
raw

180
usb

204
ttyS%d

205
cua%d

254
s3c2440-ts

Block
devices:

1 ramdisk

7 loop

31 mtdblock

同时在dev目录下自动建立了下面的节点:

/dev/v4l/video

2008-5-31

1、今天尝试着编译了webcam-server-0.50,

使用2.95.3编译器,显示不能找到jpeg库,重新编译了jpeg库,还是有些问题,干脆直接使用3.3.2的编译器;

./configure –prefix=/usr/local/arm/3.3.2/
-srcdir=/home/xiyong/vedio/webcam_server-0.50

然后修改Makefile src/Makefile

cc=/usr/local/arm/3.3.2/bin

报错:没有找到fd,

修改:webcam_server.c的那几个struct定义到main开头即可

直接make,会在src/目录下生成webcam_server文件,拷贝到/home/nfs目录下

建立节点:/dev/video0 c 81 0

运行 ./webcam_server目录

在计算机的IE浏览器中输入 http://192.168.10.199:8888/可以看到采集到的图像

点击刷新,图片会不断刷新。

五.
存在的问题和拟采取的解决方案

改变输出文件目录以后采集速度可以达到每秒4,5帧,但是一般的视频播放都要求每秒15帧以上,所以相对说来采集速度还是太慢,暂时还没有好的解决方案。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: