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



 

 

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 usbusb 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 linuxkernel目录下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)

              /*mmapvideo_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(可执行)

FlagsMAP_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->framestatframe的值来进行双缓冲,即采集一帧时处理另一帧,同时加一个外循环达到连续多帧的采集,每帧的地址都是通过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图像格式为:每个像素点包含RGB三个分量,且三分量交替排列,即(RGB);(RGB);(RGB);……这样交替出现,如果不加BMP头图像大小就为width*height*3.

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

yuv420则不一样,首先是比例,每行都只有U分量或者V分量,YUV=420402

其次排列形式也不一样,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/可以看到采集到的图像

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

 

 

 

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

改变输出文件目录以后采集速度可以达到每秒45帧,但是一般的视频播放都要求每秒15帧以上,所以相对说来采集速度还是太慢,暂时还没有好的解决方案。

 

 

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 usbusb 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 linuxkernel目录下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)

              /*mmapvideo_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(可执行)

FlagsMAP_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->framestatframe的值来进行双缓冲,即采集一帧时处理另一帧,同时加一个外循环达到连续多帧的采集,每帧的地址都是通过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图像格式为:每个像素点包含RGB三个分量,且三分量交替排列,即(RGB);(RGB);(RGB);……这样交替出现,如果不加BMP头图像大小就为width*height*3.

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

yuv420则不一样,首先是比例,每行都只有U分量或者V分量,YUV=420402

其次排列形式也不一样,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/可以看到采集到的图像

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

 

 

 

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

改变输出文件目录以后采集速度可以达到每秒45帧,但是一般的视频播放都要求每秒15帧以上,所以相对说来采集速度还是太慢,暂时还没有好的解决方案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值