servfox分析

 

看到一片关于servfox解析的文章,为了防止以后找不到,现转载到这里,感谢原文作者。

搞了一段时间的嵌入式网络视频采集与传输,基本上在服务器端采集视频用的是servfox程序包,它可以用arm-linux-gcc编译后移植到开发板上,我现在一直在做这方面的工作,用的是S3C2410的板子,这个开源程序十分不错,觉得有必要深入理解和学习一下它的源程序,网上一般没有对它的详细说明,只是简单介绍了它怎么用的,就如我当初刚开始学习嵌入式时一样看不懂这个程序,现在对其各个代码作个详细的解释,以备后忘,也希望对新手有所帮助。

       servfox主要有server.c,spcav4l.c ,spcav4l.h,utils.c ,utils.h,tcputils.c ,tcputils.h,spcaframe.h,Makefile,Makefile.fox这几个文件, arm-linux-gcc编译后可以生成servfox可执行文件,可以移植到ARM板上作为服务器端视频采集程序用。我们从server.c代码开始。


1. server.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h> 
#include <errno.h>
#include<netdb.h>
#include<sys/types.h>
#include<sys/fcntl.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>
#include <net/if_arp.h>
#include <net/if.h>
#include <sys/ioctl.h> 
#include <netinet/ether.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
//#include <signal.h>

#include "spcaframe.h"
#include "spcav4l.h"
#include "utils.h"
#include "tcputils.h"    
#include "version.h"
 
static int debug = 0; 
void grab (void);
void service (void *ir);
void sigchld_handler(int s);
struct vdIn videoIn; 

int main (int argc, char *argv[])
{

  char *videodevice = NULL;
  int grabmethod = 1;
  int format = VIDEO_PALETTE_JPEG;
  int width = 352;
  int height = 288; 
  char *separateur;
  char *sizestring = NULL; 
  int i;
  int serv_sock,new_sock;
  pthread_t w1;
  pthread_t server_th;
  int sin_size;
  unsigned short serverport = 7070;
  struct sockaddr_in their_addr;
  struct sigaction sa;

    int ret;
   
/*********************************************命令行输入参数设置************************************************/

        /********************************************************************************************

          命令行参数应该这个样子的:./servfox -g -d /dev/video0 -s 640x480 -w 7070

         *********************************************************************************************/ 
  for (i = 1; i < argc; i++)
    {
      /* skip bad arguments */
      if (argv[i] == NULL || *argv[i] == 0 || *argv[i] != '-')
    {
      continue;
    }
      if (strcmp (argv[i], "-d") == 0)

       /********************************************************************************************

          -d 参数用于设置输入视频采集设备。

       *********************************************************************************************/


    {
      if (i + 1 >= argc)
        {
          if(debug) printf ("No parameter specified with -d, aborting./n");
          exit (1);
        }
      videodevice = strdup (argv[i + 1]);

      /********************************************************************************************

             

            函数原型:extern char *strdup(char *str)

         参数说明:str待复制的字符串。  
         所在库名:#include <string.h>
         函数功能:实现复制字符串str。
         返回说明:返回复制的新字符串的指针,复制失败返回NULL,
 所需空间由malloc()分配且可以由free()释放。

          strdup()主要是拷贝字符串 str 的一个副本,由函数返回值返回, 函数返回值是一个字符型指针,它指向 字符串 str 副本的地址空间, 这个副本有自己的内存空间, 是由malloc()自动分配的, str 不相干。

       执行完这句后, char *videodevice就指向 argv[i + 1]所表示的字符串,即 /dev/video0

        与 strcpy 区别:
         
strdup不是标准的c函数, strcpy是标准的c函数

         strdup可以直接把要复制的内容复制给没有初始化的指针,因为它会自动分配空间给目的指针
         strcpy的目的指针一定是已经分配内存的指针
        

strcpy 原型: extern char *strcpy(char *dest,char *src);  用法:#include <string.h> 功能:把src所指由NULL结束的字符串复制到dest所指的数组中。 说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。 返回指向dest的指针。      

        *********************************************************************************************/


    }
      if (strcmp (argv[i], "-g") == 0)
    {
      /* Ask for read instead default  mmap */
      grabmethod = 0;
    }
       
/********************************************************************************************

          -g 参数用于读取采集到的视频的方式,用read方式而非mmap方式。

       *********************************************************************************************/

 


    if (strcmp (argv[i], "-s") == 0) {
            if (i + 1 >= argc) {
                if(debug) printf ("No parameter specified with -s, aborting./n");
                exit (1);
            }
       
/********************************************************************************************

          -s 参数用于设置视频图像帧的大小,如 640x480

       *********************************************************************************************/


            sizestring = strdup (argv[i + 1]);

            width = strtoul (sizestring, &separateur, 10);
        
/********************************************************************************************

             strtoul(将字符串转换成无符号长整型数)

             头文件:#include<stdlib.h>

 

            unsigned long strtoul( const char *nptr, char **endptr, int base ) 

      把输入的字符串转换成数字。
      参数一 字符串的起始地址。
      参数二 返回字符串有效数字的结尾地址。如 123456ff789 则返回数字6的地址。实际上返回的应该是第一个f的地址
      参数三 转化基数.

 

             函数说明: strtol ()会将参数nptr字符串根据参数base来转换成长整型数。参数base范围从2至36,或0。参数base代表采用的进制方式 ,如base值为10则采用10进制(字符串以10进制表示),若base值为16则采用16进制(字符串以16进制表示)。当base值为0时则是采用10进制做转换,但遇到如''0x''前置字符则会使用16进制做转换。一开始strtol ()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,再遇到非数字或字符串时(''/0'')结束转换,并将结果返回 。若参数endptr不为NULL,则会将遇到不合条件而终止的nptr中的字符指针由endptr返回。 
返回值:    返回转换后的长整型数,否则返回ERANGE并将错误代码存入errno中。

           若参数endptr不为NULL,则会将遇到不合条件而终止的nptr中的字符指针由endptr返回。
           参数二 返回字符串有效数字的结尾地址。如 123456ff789 则返回数字6的地址。实际上返回的应该是第一个f的地址

          char *sizestring 指向 argv[i + 1]所表示的字符串,即 640x480

          strtoul (sizestring, &separateur, 10)中遇到x结束转换,并返回结果 640给width

       *********************************************************************************************/ 

            if (*separateur != 'x') {
                if(debug) printf ("Error in size use -s widthxheight /n");
                exit (1);
            } else {
                ++separateur;
                height =strtoul (separateur, &separateur, 10); 
                if (*separateur != 0)
                    if(debug) printf ("hmm.. dont like that!! trying this height /n");
                if(debug) printf (" size width: %d height: %d /n",
                    width, height);
            }
    }


    if (strcmp (argv[i], "-w") == 0) {
            if (i + 1 >= argc) {
                if(debug) printf ("No parameter specified with -w, aborting./n");
                exit (1);
            }

      /********************************************************************************************

          -w参数用于设置端口。

       *********************************************************************************************/


            serverport = (unsigned short) atoi (argv[i + 1]);
            if (serverport < 1024  ){
            if(debug) printf ("Port should be between 1024 to 65536 set default 7070 !./n");
            serverport = 7070;
            }
    }

      if (strcmp (argv[i], "-h") == 0)
    {
      printf ("usage: cdse [-h -d -g ] /n");
      printf ("-h    print this message /n");
      printf ("-d    /dev/videoX       use videoX device/n");
      printf ("-g    use read method for grab instead mmap /n");

      printf ("-s    widthxheight      use specified input size /n");
      printf ("-w    port      server port /n");

      exit (0);
    }
    }

 

/********************************* main code主程序开始位置*************************************************/      
  printf(" %s /n",version);
  if (videodevice == NULL || *videodevice == 0)
    {
      videodevice = "/dev/video0";
    }

  memset (&videoIn, 0, sizeof (struct vdIn));

 if (init_videoIn(&videoIn, videodevice, width, height, format,grabmethod) != 0)  跟踪进入init_videoIn() 
     
    if(debug) printf (" damned encore rate !!/n");    
         /*****************************************************************************************
                  spcav4l.c中:
                  int init_videoIn (struct vdIn *vd, char *device, int width, int height,
                  int format, int grabmethod)
                  struct vdIn videoIn;  //在spcav4l.h中定义
                  videodevice = "/dev/video0";
                 int width = 352;
                 int height = 288;
                 int format = VIDEO_PALETTE_JPEG;
                 int grabmethod = 1;

                请进入下面的 跟踪进入 init_videoIn() ,对其进 行详细说明,我们来看看它里面做了些什么事情。 
        ***********************************************************************************************/ 
        main()函数还没有完,请看嵌入式网络视频采集源程序servfox解析02

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


 跟踪进入 init_videoIn()

/*******************************************************************************************************

    init_videoIn()函数在spcav4l.c中定义,这个函数主要用来初始化视频采集设备。

int   

init_videoIn (struct vdIn *vd, char *device, int width, int height,int format, int grabmethod)
{

     /*************************************************************************************

          它的几个参数是由server.c中传递进来的:

          if (init_videoIn(&videoIn, videodevice, width, height, format,grabmethod) != 0)

             其中

                videoIn;在spcav4l.h中定义的struct vdIn videoIn结构体
                 videodevice = "/dev/video0";   //前面输入的
                 int width = 352;(640)
                 int height = 288;(320)
                 int format = VIDEO_PALETTE_JPEG;
                 int grabmethod = 1;

      **************************************************************************************/
  int err = -1;
  int i;
  if (vd == NULL || device == NULL)
    return -1;
  if (width == 0 || height == 0)
    return -1;
  if(grabmethod < 0 || grabmethod > 1)
      grabmethod = 1; //read by default,设置read方式为默认方式


  /*************************************************************************************

         // check format检查格式,

    我们先来看一下 struct vdIn结构体,在spcav4l.h中定义 ,它是对 Video4Linux视频设备数据

    结构的定义。 
        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;    
    int cameratype ;      // 是否能capture,彩色还是黑白,是否  能裁剪等等 
    char *cameraname;   //设备名称 
    char bridge[9];
    int sizenative; // available size in jpeg
    int sizeothers;    // others palette 
    int palette; // available palette
    int norme ; // set spca506 usb video grabber
    int channel ; // set spca506 usb video grabber 
//信号源个数 
    int grabMethod ;
    unsigned char *pFramebuffer;   
//指向内存映射的指针 
    unsigned char *ptframe[4];   //指向压缩后的帧的指针数组 
    int framelock[4];
    pthread_mutex_t grabmutex;   
// 视频采集线程和传输线程的互斥信号 
    int framesizeIn ;   // 视频帧的大小 
    volatile int frame_cour;   // 指向压缩后的帧的指针数组下标


    int bppIn;   // 采集的视频帧的BPP

               --- --------------------------------------------------------------------------------------------

 

                我们在流媒体文件的编码过程中总是试图得到最佳的图像质量,这可以通过调整编码过程中帧的         大小或编码码率来实现。在此可通过位每像素(BPP,BitPerPixel)来确定帧的大小或编码码率,从而            得到理想的图像质量。

        位每像素(BPP)是通过调整帧的大小或编码码率得到最佳图像质量的一个重要参考指标.

        如果已确认要编码的视频图像帧的大小,但不确定采用何种编码码率可以得到最佳的编码图像质量,        可采用如公式1确定编码码率。

        公式1:编码码率=位每像素×帧率×宽度×高度

            (Bitrate=BPP×FrameRate×Width×Height )

        ----------------------------------------------------------------------------------------------------
    int  hdrwidth;   // 采集的视频帧的宽度 
    int  hdrheight;   // 采集的视频帧的高度 
    int  formatIn;   
//采集的视频帧的格式 
    int signalquit;    
//停止视频采集的信号 
    };
    

  **************************************************************************************/
  vd->videodevice = NULL;
  vd->cameraname = NULL;
  vd->videodevice = NULL;
  vd->videodevice = (char *) realloc (vd->videodevice, 16);
  vd->cameraname = (char *) realloc (vd->cameraname, 32);

  /*************************************************************************************

       realloc

原型:extern void *realloc(void *mem_address, unsigned int newsize); 用法:#include <stdlib.h> 有些编译器需要#include <alloc.h> 功能:改变mem_address所指内存区域的大小为newsize长度 。 说明:如果重新分配成功则返回指向被分配内存的指针 ,否则返回空指针NULL。 当内存不再使用时,应使用free()函数将内存块释放。 注意:这里原始内存中的数据还是保持不变的。

   **************************************************************************************/
  snprintf (vd->videodevice, 12, "%s", device);

   /*************************************************************************************

      把server.c中传递进来的device指向的videodevice = "/dev/video0"复制到 vd->videodevice中

    使 vd->videodevice就指向 "/dev/video0"

   **************************************************************************************/

  
  if(debug) printf("video %s /n",vd->videodevice);  
  memset (vd->cameraname, 0, sizeof (vd->cameraname));
  memset(vd->bridge, 0, sizeof(vd->bridge));
  vd->signalquit = 1;    //信号设置
  vd->hdrwidth = width;//传递宽高
  vd->hdrheight = height;


  /*          compute the max frame size   */
  vd->formatIn = format;     //传进来的 format = VIDEO_PALETTE_JPEG;
  vd->bppIn = GetDepth (vd->formatIn); 

   /**************************************************************************************

    GetDepth()是 前面声明的函数, spcav4l.c 后面有定义

      static int
    GetDepth (int format)
    {
      int depth;
  switch (format) 
    {
   
   case VIDEO_PALETTE_JPEG:
      {
    depth = 8;        
      } 
      break;
    case VIDEO_PALETTE_RAW:
      {
    depth = 8;        
      }
      break;
    case VIDEO_PALETTE_YUV420P:
      {
    depth = (8 * 3) >> 1;
      }
      break;
    case VIDEO_PALETTE_RGB565:
      depth = 16;
      break;
    case VIDEO_PALETTE_RGB24:
      depth = 24;
      break;
    case VIDEO_PALETTE_RGB32:
      {
    depth = 32;
      }
      break;
    default:
      depth = -1;
      break;
    }
  return depth; 

     
    *****************************************************************************************/

  vd->grabMethod = grabmethod;        //mmap or read 
  vd->pFramebuffer = NULL;


  /* init and check all setting */
  err = init_v4l (vd);   
跟踪进入 init_v4l (vd)

   **************************************************************************************/
     
init_v4l (vd)是 前面声明的函数,后面有定义,执行videodevice = "/dev/video0"的打开,
       我们跟踪进入
 
init_v4l (vd)来看看,看看它里面做了哪些工作

       跟踪进入 init_v4l (vd)

      

   **************************************************************************************/

       init_videoIn()函数还没有完,请看下一篇:嵌入式网络视频采集源程序servfox解析03 

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


跟踪进入 init_v4l (vd)

 

/****************************************************************************************************

init_v4l (vd)是初始化V4L视频设备的函数,在spcav4l.c中定义:

static int
init_v4l (struct vdIn *vd)
{
  int f; 
  int erreur = 0;
  int err;
  if ((vd->fd = open (vd->videodevice, O_RDWR)) == -1)//打开 视频设备 
    exit_fatal ("ERROR opening V4L interface");

  if (ioctl (vd->fd, VIDIOCGCAP, &(vd->videocap)) == -1) 
    exit_fatal ("Couldn't get videodevice capability");
   /*************************************************************************************************
        ioctl用于向设备发控制和配置命令 ,有些命令也需要读写一些数据,但这些数据是不能用read/write读写的,称为Out-of-band数据。也就是说,read/write读写的数据是in-band数据,是I/O操作的主体,而ioctl命令传送的是控制信息,其中的数据是辅助的数据。ioctl是设备驱动程序中对设备的I/O通道进行管理的函数,所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如,在串口线上收发数据通过read/write操作,而串口的波特率、校验位、停止位通过ioctl设置,A/D转换的结果通过read读取,而A/D转换的精度和工作频率通过ioctl设置。
    ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数控制设备的I/O通道。
    如果不用ioctl的话,也可以实现对设备I/O通道的控制,但那就是蛮拧了。例如,我们可以在驱动程序中实现write的时候检查一下是否有特殊约定的数据流通过,如果有的话,那么后面就跟着控制命令(一般在socket编程中常常这样做)。但是如果这样做的话,会导致代码分工不明,程序结构混乱,程序员自己也会头昏眼花的。
  
  所以,我们就使用ioctl来实现控制的功能 。要记住,用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。 
    int ioctl(int fd, ind cmd, …);
    其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。

   

    调用函数ioctl (vd->fd, VIDIOCGCAP, &(vd->videocap))成功后可读取vd->capability各分量 , 读video_capability 中信息包括设备名称,支持最大最小分辨率,信号源信息等。 

    video_capability是Video4linux支持的数据结构, 在前面定义:

    struct vdIn {
    int fd;      //设备 描述符, 文件描述符 
    char *videodevice ; //设备, 视频捕捉接口文件 
    struct video_mmap vmmap; 
    struct video_capability videocap;   // 包含设备的基本信息(设备名称、支持的最大最小分辨率、信号源     .....................................

    }


video_capability 包含设备的基本信息(设备名称、支持的最大最小分辨率、信号源信息等),包含的分量:
•name[32]   //设备名称
•maxwidth ,maxheight,minwidth,minheight
•Channels //信号源个数
•type    //是否能capture,彩色还是黑白,是否能裁剪等等。值如VID_TYPE_CAPTURE等


struct video_capability
{
        char name[32];
        int type;
        int channels;        /* Num channels */
        int audios;        /* Num audio devices */
        int maxwidth;        /* Supported width */
        int maxheight;        /* And height */
        int minwidth;        /* Supported width */
        int minheight;        /* And height */
};
    name是摄像头的名字,maxwidth maxheight是摄像头所能获取的最大图像大小,用像素作单位。 
    在程序中,通过ioctl函数的VIDIOCGCAP控制命令读写设备通道已获取这个结构

*****************************************************************************************************/ 

  if(debug) printf ("Camera found: %s /n", vd->videocap.name);


  snprintf (vd->cameraname, 32, "%s", vd->videocap.name);

   /*********************************************************************************************

      我们通过在 Video4Linux视频设备数据结构中: 
        struct vdIn {
    int fd;      //设备 描述符, 文件描述符 
    char *videodevice ; //设备, 视频捕捉接口文件 
    struct video_mmap vmmap; 
    struct video_capability videocap;

      ............................................

    char *cameraname;   //设备名称

    }

       定义 struct video_capability videocap这个结构体,通过上面的 ioctl (vd->fd, VIDIOCGCAP, &(vd->videocap))获得视频采集设备的名称后传给 vd->cameraname。 
    *********************************************************************************************/
  erreur = GetVideoPict (vd);

  /*********************************************************************************************

    GetVideoPict()也在spcav4l.c中定义:

       static int
GetVideoPict (struct vdIn *vd)
{
  if (ioctl (vd->fd, VIDIOCGPICT, &vd->videopict) < 0)
    exit_fatal ("Couldnt get videopict params with VIDIOCGPICT");
        -------------------------------------------------------------

             跟 ioctl (vd->fd, VIDIOCGCAP, &(vd->videocap))类似, ioctl (vd->fd, VIDIOCGPICT,                 &vd->videopict) 获取摄象头缓冲区中video_picture 中各 分量的值。

          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_picture videopict这个结构体:

               

                struct video_picture

                {

                     __u16     brightness;

                __u16     hue;

                __u16     colour;

                __u16     contrast;

                __u16     whiteness;       /* Black and white only */

                __u16     depth;            /* Capture depth */

                __u16   palette;    /* Palette in use */

       }

                    video_picture 设备采集的图象的各种属性
                    •brightness 0~65535  亮度 
                    •hue 
                    •colour 
                    •contrast    对比度 
                    •whiteness 
                    •depth // 24   色深 
                    •palette //VIDEO_PALETTE_RGB24   调色板

                picture 结构包括了亮度,对比度,色深,调色板等等信息

        -------------------------------------------------------------

  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;
}

    这个函数是为了获得 摄象头 采集到的图象 缓冲区中video_picture 中各分量的值

    如果要对 对采集图象的各种属性进行设置,可分为两步进行, 首先获取摄象头缓冲区中video_picture中信息调用函数ioctl(vd->fd, VIDIOCGPICT, &(vd->picture));然后改变video_picture中分量的值, 调用 ioctl (vd->fd, VIDIOCSPICT, &vd->videopict) 为vd->videopict分量赋新值。

  *********************************************************************************************/


   if (ioctl (vd->fd, VIDIOCGCHAN, &vd->videochan) == -1)


    {
      if(debug) printf ("Hmm did not support Video_channel/n");
      vd->cameratype = UNOW;
    }

     /*********************************************************************************************

          同样, ioctl (vd->fd, VIDIOCGCHAN, &vd->videochan)可以获得 各个信号源的属性的信息

             要 上面的 struct vdIn中定义的 struct video_channel videochan

          video_channel         关于各个信号源的属性
            Channel //信号源的编号
            name
            tuners
            Type     VIDEO_TYPE_TV  |  IDEO_TYPE_CAMERA
            Norm制式

          struct video_channel在 v4l API 中定义:

          struct video_channel
            {
                int channel;
                char name[32];
                int tuners;
                __u32  flags;
                #define VIDEO_VC_TUNER        1    /* Channel has a tuner */
                #define VIDEO_VC_AUDIO        2    /* Channel has audio */
                __u16  type;
                # define VIDEO_TYPE_TV        1
                #define VIDEO_TYPE_CAMERA    2
                __u16 norm;            /* Norm set by channel */
                }; 
                成员channel代表输入源,通常,0: television 1:composite1 2:s-video
                name 表示该输入源的名称。
                norm 表示制式,通常,0:pal 1:ntsc 2:secam 3:auto

             在linux下,在 /usr/include/linux /videodev.h中有详细定义。

          vd->cameratype:在

            struct vdIn {

                   .................................

                     int cameratype ;      // 是否能capture,彩色还是黑白,是否  能裁剪等等
                    char *cameraname;   //设备名称
                    char bridge[9];

                ........................................

             }

            中定义。

          **********************************************************************************************/
    
  else
    {
    if (vd->videochan.name){
      if(debug) printf ("Bridge found: %s /n", vd->videochan.name);
      snprintf (vd->bridge, 9, "%s", vd->videochan.name);
      vd->cameratype = GetStreamId (vd->bridge);
      }

     /*********************************************************************************************

          进入:GetStreamId ()函数定义在spcav4l.c中:

      static int
  GetStreamId (const char *BridgeName)
   {
          int i = -1;
          int match = -1;
/* return Stream_id otherwhise -1 */
  if ((match = isSpcaChip (BridgeName)) < 0)
    {
      if(debug) printf ("Not an Spca5xx Camera !!/n");
      return match;
    } 

   

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    isSpcaChip也定义在spcav4l.c中:在  GetStreamId()的上面。

static int
isSpcaChip (const char *BridgeName)
{
  int i = -1;
  int find = -1;
  int size = 0;
 
  /* Spca506 return more with channel video, cut it */
 
  /* return Bridge otherwhise -1 */
  for (i = 0; i < MAX_BRIDGE -1; i++)
    {
    size = strlen (Blist[i].name) ;

      ===========================================

          MAX_BRIDGE在spcav4l.h中定义,是一个枚举成员

          static struct bridge_list Blist[]={
    Blist[]数组在spcav4l.c前面定义:


       static struct bridge_list Blist[]={
    {BRIDGE_SPCA505,"SPCA505"},
    {BRIDGE_SPCA506,"SPCA506"},
    {BRIDGE_SPCA501,"SPCA501"},
    {BRIDGE_SPCA508,"SPCA508"},
    {BRIDGE_SPCA504,"SPCA504"},
    {BRIDGE_SPCA500,"SPCA500"},
    {BRIDGE_SPCA504B,"SPCA504B"},
    {BRIDGE_SPCA533,"SPCA533"},
    {BRIDGE_SPCA504C,"SPCA504C"},
    {BRIDGE_SPCA561,"SPCA561"},
    {BRIDGE_SPCA536,"SPCA536"},
    {BRIDGE_SONIX,"SN9C102"},
    {BRIDGE_ZR364XX,"ZR364XX"},
    {BRIDGE_ZC3XX,"ZC301-2"},
    {BRIDGE_CX11646,"CX11646"},
    {BRIDGE_TV8532,"TV8532"},
    {BRIDGE_ETOMS,"ET61XX51"},
    {BRIDGE_SN9CXXX,"SN9CXXX"},
    {BRIDGE_MR97311,"MR97311"},
    {BRIDGE_UNKNOW,"UNKNOW"},
    {-1,NULL}
};

    static struct bridge_list结构体 

      =========================================== 
    if(debug) printf ("is_spca %s size %d /n",Blist[i].name,size);
      if (strncmp (BridgeName, Blist[i].name, size) == 0)
    {  
    find = i;
      break;
    }
    }

  return find;
}

    函数原型:extern int strcmp(char *str1,char * str2,int n)

    参数说明:str1为第一个要比较的字符串,str2为第二个要比较的字符串,n为指定的str1与str2的比较的    字符数。    
    所在库名:#include <string.h>
    函数功能:比较字符串str1和str2的前n个字符。 
    返回说明:返回整数值:当str1<str2时,返回值<0; 当str1=str2时,返回值=0; 当str1>str2时,返回值>0。

………………………………………………………………………………………………… 
  switch (match)
    {
    case BRIDGE_SPCA505:
    case BRIDGE_SPCA506:
      i = YYUV;
      break;
    case BRIDGE_SPCA501:
      i = YUYV;
      break;
    case BRIDGE_SPCA508:
      i = YUVY;
      break;
    case BRIDGE_SPCA536:
    case BRIDGE_SPCA504:
    case BRIDGE_SPCA500:
    case BRIDGE_SPCA504B:
    case BRIDGE_SPCA533:
    case BRIDGE_SPCA504C:
    case BRIDGE_ZR364XX:
    case BRIDGE_ZC3XX:
    case BRIDGE_CX11646:
    case BRIDGE_SN9CXXX:
    case BRIDGE_MR97311:  
      i = JPEG;
      break;
    case BRIDGE_ETOMS:
    case BRIDGE_SONIX:
    case BRIDGE_SPCA561:
    case BRIDGE_TV8532:
      i = GBRG;
      break;
    default:
      i = UNOW; // -1;
      if(debug) printf ("Unable to find a StreamId !!/n");
      break;

    }
  return i;
}

       返回的是视频图像信号的格式。

       现在退出GetStreamId(),回到init_v4l (struct vdIn *vd)中:

      *********************************************************************************************/

   回到init_v4l (struct vdIn *vd)中, 请看 嵌入式网络视频采集源程序servfox解析04 

 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 


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值