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