else
{
if(debug) printf ("Bridge not found not a spca5xx Webcam Probing the hardware !!/n");
vd->cameratype = UNOW;
}
}
/* Only jpeg webcam allowed */
if(vd->cameratype != JPEG) {
exit_fatal ("Not a JPEG webcam sorry Abort !");
}
if(debug) printf ("StreamId: %d Camera/n", vd->cameratype);
/* probe all available palette and size Not need on the FOX always jpeg
if (probePalette(vd ) < 0) {
exit_fatal ("could't probe video palette Abort !");
}
if (probeSize(vd ) < 0) {
exit_fatal ("could't probe video size Abort !");
}
err = check_palettesize(vd);
if(debug) printf (" Format asked %d check %d/n",vd->formatIn, err);
*/
vd->videopict.palette = vd->formatIn; //采集的视频帧的格式,调色板如
VIDEO_PALETTE_RGB24
//vd->formatIn = format=VIDEO_PALETTE_JPEG;
vd->videopict.depth = GetDepth (vd->formatIn);
vd->bppIn = GetDepth (vd->formatIn);
//vd->framesizeIn = (vd->hdrwidth * vd->hdrheight * vd->bppIn) >> 3; // here alloc the output ringbuffer
vd->framesizeIn = (vd->hdrwidth * vd->hdrheight >> 2 ); // here alloc the output ringbuffer(环形缓冲 区) jpeg only,
// 视频帧的大小
erreur = SetVideoPict (vd);
/*********************************************************************************
进入:SetVideoPict(),在spcav4l.c中定义:
static int
SetVideoPict (struct vdIn *vd)
{
if (ioctl (vd->fd, VIDIOCSPICT, &vd->videopict) < 0)
exit_fatal ("Couldnt set videopict params with VIDIOCSPICT");
if(debug) printf ("VIDIOCSPICT brightnes=%d hue=%d color=%d contrast=%d whiteness=%d"
"depth=%d palette=%d/n", vd->videopict.brightness,
vd->videopict.hue, vd->videopict.colour, vd->videopict.contrast,
vd->videopict.whiteness, vd->videopict.depth,
vd->videopict.palette);
return 0;
}
这里其实没有设置采集图像的亮度,对比度,色深,调色板等等信息,只是用ioctl获取了一下,采用视频设备本身默认的,我们可以输出这些属性看看。
退出SetVideoPict(),返回到init_v4l
**********************************************************************************/
erreur = GetVideoPict (vd);
/*********************************************************************************
进入:GetVideoPict (vd),在spcav4l.c中定义:
static int
GetVideoPict (struct vdIn *vd)
{
if (ioctl (vd->fd, VIDIOCGPICT, &vd->videopict) < 0)
exit_fatal ("Couldnt get videopict params with VIDIOCGPICT");
if(debug) printf ("VIDIOCGPICT brightnes=%d hue=%d color=%d contrast=%d whiteness=%d"
"depth=%d palette=%d/n", vd->videopict.brightness,
vd->videopict.hue, vd->videopict.colour, vd->videopict.contrast,
vd->videopict.whiteness, vd->videopict.depth,
vd->videopict.palette);
return 0;
}
这里其实可以得到采集图像的亮度,色调,色深,对比度,色度,深度,调色板等等信息,只是用ioctl获取了一下,采用视频设备本身默认的,我们可以输出这些属性看看。
退出GetVideoPict (vd),返回到init_v4l
**********************************************************************************/
if (vd->formatIn != vd->videopict.palette ||
vd->bppIn != vd->videopict.depth)
exit_fatal ("could't set video palette Abort !");
if (erreur < 0)
exit_fatal ("could't set video palette Abort !");
if (vd->grabMethod)
{
if(debug) printf (" grabbing method default MMAP asked /n");
/*********************************************************************************
在前面的init_videoIn中:
grabmethod = 1; //read by default;
vd->grabMethod = grabmethod; //mmap or read
采用mmap方式截取图象,视频数据的读取,这里我们简单介绍一下获得图像的两种方式
初始化好上面的v4l结构后,摄像头采集的视频数据可以有两种方式来读取:
分别是直接读取设备和使用mmap内存映射 ,而通常大家使用的方法都是后者
1).直接读取设备
直接读设备的方式就是使用read()函数 ,我们先前定义的
extern int v4l_grab_picture(v4l_device *, unsigned int);函数就是完成这个工作的,它的实现也很简单。
int v4l_grab_picture(v4l_device *vd, unsighed int size)
{
if(read(vd-fd,&(vd->map),size)==0)return -1;
return 0;
}
该函数的使用也很简单,就是给出图像数据的大小,vd->map所指向的数据就是图像数据。而图像数据的大小你要根据设备的属性自己计算获得。这就是下面的/* read method */
2).使用mmap内存映射来获取图像
在这部分涉及到下面几个函数,它们配合来完成最终图像采集的功能。
extern int v4l_mmap_init(v4l_device *);该函数把摄像头图像数据映射到进程内存中,也就是只要使用vd->map指针就可以使用 采集到的图像数据 (下文详细说明)
extern int v4l_grab_init(v4l_device *, int, int);该函数完成图像采集前的初始化工作。
extern int v4l_grab_frame(v4l_device *, int);该函数是真正完成图像采集的一步,在本文使用了一个通常都会使用的一个小技巧,可以在处理一帧数据时同时采集下一帧的数据,因为通常我们使用的摄像头都可以至少存储两帧的数据。
extern int v4l_grab_sync(v4l_device *);该函数用来完成截取图像的同步工作,在截取一帧图像后调用,返回表明一帧截取结束
mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必在调用read(),write()等操作。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时访问进程B对共享内存中数据的更新,反之亦然。
采用共享内存通信的一个显而易见的好处是减少I/O操作提高读取效率,因为使用mmap后进程可以直接读取内存而不需要任何数据的拷贝。
mmap的函数原型如下
void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset )
addr:共享内存的起始地址,一般设为0,表示由系统分配。
len:指定映射内存的大小。在我们这里,该值为摄像头mbuf结构体的size值,即图像数据的总大小。
port:指定共享内存的访问权限 PROT_READ(可读),PROT_WRITE(可写)
flags:一般设置为MAP_SHARED
fd:同享文件的文件描述符。
用内存映射法一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。
与 read()方式相比, mmap()方式通过把设备文件映射到内存,绕过了内核缓冲区,加速了 I/O访问。完成内存映射之后,就可以用 mmap()方式实现对内存映射区域视频数据的单帧采集。此方式下真正做视频截取的为 VIDIOCMCAPTURE,调用函数 ioctl(_fd, VIDIOCMCAPTURE,&mmap),激活设备并真正开始一帧图像的截取,是非阻塞的,接着调用 ioctl(_fd,VIDIOCSYNC,&frame)函数等待一帧图像截取结束,成功返回表示一帧截取已完成,接着可以做下一次的 VIDIOCMCAPTURE操作。
*************************************************************************************/
// MMAP VIDEO acquisition
memset (&(vd->videombuf), 0, sizeof (vd->videombuf));
if (ioctl (vd->fd, VIDIOCGMBUF, &(vd->videombuf)) < 0)
{
perror (" init VIDIOCGMBUF FAILED/n");
}
/*********************************************************************************
vd->videopict在下面定义:
struct vdIn {
int fd; //设备 描述符, 文件描述符
char *videodevice ; //设备, 视频捕捉接口文件
struct video_mmap vmmap;
struct video_capability videocap; // 包含设备的基本信息(设备名称、支持的最大最小分辨率、信 号源信息等)
int mmapsize;
struct video_mbuf videombuf ; //映射的帧信息,实际是映射到摄像头存储缓冲区的帧信息,包括帧 的大小(size),最多支持的帧数(frames) 每帧相对基址的偏移 (offset)
struct video_picture videopict; //采集图像的各种属性
struct video_window videowin;
struct video_channel videochan;
...................................................................................
}
我们来看看struct video_mbuf videombuf这个结构体:
struct video_mbuf
{
int size; /* Total memory to map */帧的大小
int frames; /* Frames */最多支持的帧数
int offsets[VIDEO_MAX_FRAME];每帧相对基址的偏移
};
这里用ioctl来获取摄象头存储缓冲区的帧信息。
******************************************************************************************************************/
if(debug) printf ("VIDIOCGMBUF size %d frames %d offets[0]=%d offsets[1]=%d/n",
vd->videombuf.size, vd->videombuf.frames,
vd->videombuf.offsets[0], vd->videombuf.offsets[1]);
vd->pFramebuffer =
(unsigned char *) mmap (0, vd->videombuf.size, PROT_READ | PROT_WRITE,
MAP_SHARED, vd->fd, 0);
vd->mmapsize = vd->videombuf.size;
vd->vmmap.height = vd->hdrheight;
vd->vmmap.width = vd->hdrwidth;
vd->vmmap.format = vd->formatIn;
/*********************************************************************************
将mmap与video_mbuf绑定,把摄象头对应的设备文件映射到内存区,成功调用后设备文件内容映射到内存区,返回的映象内存区指针给vd->pFramebuffer,失败时返回-1。
•void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset )
addr:共享内存的起始地址,一般设为0,表示由系统分配。
len:映射到调用进程地址空间的字节数,即指定映射内存的大小。在我们这里,该值为摄像 头video_mbuf结构体的size值,即图像数据帧的总大小。它从被映射文件开头offset个 字节开始算起。
prot:指定共享内存的访问权限 PROT_READ(可读), PROT_WRITE (可写), PROT_EXEC (可执行)
flags :由MAP_SHARED和MAP_PRIVATE中必选一个,MAP_ FIXED不推荐使用addr
fd:共享文件的文件描述符
offset:一般设为0
mmap( ) 返回值是系统实际分配的起始地址
vd->mmapsize = vd->videombuf.size;
vd->vmmap.height = vd->hdrheight;
vd->vmmap.width = vd->hdrwidth;
vd->vmmap.format = vd->formatIn;
上面几行是修改vd->vmmap中的设置,例如设置图象帧的大小,垂直水平分辨率,彩色显示 格式。
*********************************************************************************/
for (f = 0; f < vd->videombuf.frames; f++)
{
vd->vmmap.frame = f;//当前帧
if (ioctl (vd->fd, VIDIOCMCAPTURE, &(vd->vmmap)))
{
perror ("cmcapture");
}
}
vd->vmmap.frame = 0;
}
/*********************************************************************************
mmap方式下真正做视频截取的 VIDIOCMCAPTURE,上面是循环采集 。
ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap)) ;
•若调用成功,则激活设备真正开始一帧的截取,是非阻塞的,
•是否截取完毕留给VIDIOCSYNC来判断
可以调用VIDIOCSYNC等待一帧截取结束
if(ioctl(vd->fd, VIDIOCSYNC, &frame) < 0)
{
perror("v4l_sync:VIDIOCSYNC");
return -1;
}
若成功,表明一帧截取已完成。可以开始做下一次 VIDIOCMCAPTURE
frame是当前截取的帧的序号。
这里没采用VIDIOCSYNC。
****关于双缓冲:
•video_bmuf bmuf.frames = 2;
•一帧被处理时可以采集另一帧
*********************************************************************************/
else
{
/* read method 直接读取方式*/
/* allocate the read buffer */
vd->pFramebuffer =
(unsigned char *) realloc (vd->pFramebuffer, (size_t) vd->framesizeIn);
if(debug) printf (" grabbing method READ asked /n");
if (ioctl (vd->fd, VIDIOCGWIN, &(vd->videowin)) < 0)
perror ("VIDIOCGWIN failed /n");
vd->videowin.height = vd->hdrheight;
vd->videowin.width = vd->hdrwidth;
if (ioctl (vd->fd, VIDIOCSWIN , &(vd->videowin)) < 0)
perror ("VIDIOCSWIN failed /n");
if(debug) printf ("VIDIOCSWIN height %d width %d /n",
vd->videowin.height, vd->videowin.width);
}
vd->frame_cour = 0;
return erreur;
}
到此,V4L视频设备的初始化工作完成,现在我们退出init_v4l()函数,进入init_videoIn()函数,我们看看它后面又是怎么工作的。
*********************************************************************************/
进入init_videoIn()函数后面部分,请看:嵌入式网络视频采集源程序servfox解析05