嵌入式网络视频采集源程序servfox解析04

来源:互联网 发布:mac如何输入罗马数字 编辑:程序博客网 时间:2024/04/30 03:03

  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