OV511视频采集设计文档 (二)

301 篇文章 0 订阅
271 篇文章 2 订阅

作者:曾永刚

转自:http://zyg0227.blog.51cto.com/1043164/316940


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 分配内存空间,程序开始时分配即可。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值