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