Mjpeg-streamer源码分析(二)

2019-07-13 01:52发布

                                         input_uvc.c源码分析
-----------------------------------------------------------------------------------------------------------------------
重要函数解析:
 char *strtok_r(char *str, const char *delim, char **saveptr);
 
与线程相关的函数:
 线程可以安排它推出时需要调用的函数,这与进程可以用atexit函数安排进程退出时需要调用的函数是类似的.
 这样的函数称为线程清理处理程序,线程可以建立多个清理处理程序.处理程序记录在栈内,也就是说它们的调
 用顺序与它们的注册顺序相反
 #include
 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
 int pthread_detach(pthread_t thread);
 void pthread_cleanup_push(void (*routine)(void *),void *arg);
 void pthread_cleanup_pop(int execute);
 
-----------------------------------------------------------------------------------------------------------------------
分析input_uvc.c主要分析四个函数:
input_init() input_stop() input_run() input_cmd()
-----------------------------------------------------------------------------------------------------------------------
现在来分析一下input_init()函数:
 首先解析命令:将param->parameter_string字符串形式的命令转换为一个argv[MAX_ARGUMENTS]字符串数组;
 其次就是用getopt_long_only()解析命令;
 保留指向global全局变量的指针:
   pglobal = param->global;
 分配一个webcam的结构体:
  videoIn = malloc(sizeof(struct vdIn));
   struct vdIn {
                  int fd;
                  char *videodevice;
                  char *status;
                  char *pictName;
                  struct v4l2_capability cap;
                  struct v4l2_format fmt;
                  struct v4l2_buffer buf;
                  struct v4l2_requestbuffers rb;
                  void *mem[NB_BUFFER];
                  unsigned char *tmpbuffer;
                  unsigned char *framebuffer;
                  int isstreaming;
                  int grabmethod;
                  int width;
                  int height;
                  int fps;
                  int formatIn;
                  int formatOut;
                   int framesizeIn;
                  int signalquit;
                  int toggleAvi;
                  int getPict;
                  int rawFrameCapture;
                  /* raw frame capture */
                  unsigned int fileCounter;
                  /* raw frame stream capture */
                  unsigned int rfsFramesWritten;
                  unsigned int rfsBytesWritten;
                  /* raw stream capture */
                  FILE *captureFile;
                  unsigned int framesWritten;
                  unsigned int bytesWritten;
                  int framecount;
                  int recordstart;
                  int recordtime;
          };
 结构体成员清零:
   memset(videoIn, 0, sizeof(struct vdIn));
 显示我们设置的参数:
  IPRINT("Using V4L2 device.: %s ", dev);
  IPRINT("Desired Resolution: %i x %i ", width, height);
  IPRINT("Frames Per Second.: %i ", fps);
  IPRINT("Format............: %s ", (format==V4L2_PIX_FMT_YUYV)?"YUV":"MJPEG");
  if ( format == V4L2_PIX_FMT_YUYV )
   IPRINT("JPEG Quality......: %d ", gquality);
 初始化videoIn结构体:
  init_videoIn(videoIn, dev, width, height, fps, format, 1);
   int init_videoIn(struct vdIn *vd, char *device, int width, int height, int fps, int format, int grabmethod)
    {
      if (vd == NULL || device == NULL)
        return -1;
      if (width == 0 || height == 0)
        return -1;
      if (grabmethod < 0 || grabmethod > 1)
        grabmethod = 1;  //mmap by default;
      vd->videodevice = NULL;
      vd->status = NULL;
      vd->pictName = NULL;
      vd->videodevice = (char *) calloc (1, 16 * sizeof (char)); /* calloc分配并初始化为零 */
      vd->status = (char *) calloc (1, 100 * sizeof (char));
      vd->pictName = (char *) calloc (1, 80 * sizeof (char));
      snprintf (vd->videodevice, 12, "%s", device);
      vd->toggleAvi = 0;
      vd->getPict = 0;
      vd->signalquit = 1;
      vd->width = width;
      vd->height = height;
      vd->fps = fps;
      vd->formatIn = format;
      vd->grabmethod = grabmethod;
      if (init_v4l2 (vd) < 0) {
        fprintf (stderr, " Init v4L2 failed !! exit fatal ");
        goto error;;
      }
      /* alloc a temp buffer to reconstruct the pict */
      vd->framesizeIn = (vd->width * vd->height << 1);
      switch (vd->formatIn) {
      case V4L2_PIX_FMT_MJPEG:
        vd->tmpbuffer = (unsigned char *) calloc(1, (size_t) vd->framesizeIn);
        if (!vd->tmpbuffer)
          goto error;
        vd->framebuffer =
            (unsigned char *) calloc(1, (size_t) vd->width * (vd->height + 8) * 2);
        break;
      case V4L2_PIX_FMT_YUYV:
        vd->framebuffer = (unsigned char *) calloc(1, (size_t) vd->framesizeIn);
        break;
      default:
        fprintf(stderr, " should never arrive exit fatal !! ");
        goto error;
        break;
      }
      if (!vd->framebuffer)
        goto error;
      return 0;
    error:
      free(vd->videodevice);
      free(vd->status);
      free(vd->pictName);
      close(vd->fd);
      return -1;
    }
 对摄像头的调整:
  if (dynctrls)
   initDynCtrls(videoIn->fd);
 最后执行input_cmd函数:
   input_cmd(led, 0);
 
到此,inpu_init()函数结束,这个函数太长了~~~~~~~~~~
-----------------------------------------------------------------------------------------------------------------------
现在开始分析input_cmd()函数:
  int input_cmd(in_cmd_type cmd, int value) {
    int res=0;
    static int pan=0, tilt=0, pan_tilt_valid=-1;
    static int focus=-1;
    const int one_degree = ONE_DEGREE;
    /* certain commands do not need the mutex */
    if ( cmd != IN_CMD_RESET_PAN_TILT_NO_MUTEX )
      pthread_mutex_lock( &controls_mutex );
    switch (cmd) {
      case IN_CMD_HELLO:
        fprintf(stderr, "Hello from input plugin ");
        break;
      case IN_CMD_RESET:
        DBG("about to reset all image controls to defaults ");
        res = v4l2ResetControl(videoIn, V4L2_CID_BRIGHTNESS);
        res |= v4l2ResetControl(videoIn, V4L2_CID_CONTRAST);
        res |= v4l2ResetControl(videoIn, V4L2_CID_SATURATION);
        res |= v4l2ResetControl(videoIn, V4L2_CID_GAIN);
        if ( res != 0 ) res = -1;
        break;
      case IN_CMD_RESET_PAN_TILT:
      case IN_CMD_RESET_PAN_TILT_NO_MUTEX:
        DBG("about to set pan/tilt to default position ");
        if ( uvcPanTilt(videoIn->fd, 0, 0, 3) != 0 ) {
          res = -1;
          break;
        }
        pan_tilt_valid = 1;
        pan = tilt = 0;
        sleep(4);
        break;
      case IN_CMD_PAN_SET:
        DBG("set pan to %d degrees ", value);
        /* in order to calculate absolute positions we must check for initialized values */
        if ( pan_tilt_valid != 1 ) {
          if ( input_cmd(IN_CMD_RESET_PAN_TILT_NO_MUTEX, 0) == -1 ) {
            res = -1;
            break;
          }
        }
        /* limit pan-value to min and max, multiply it with constant "one_degree" */
        value = MIN(MAX(value*one_degree, MIN_PAN), MAX_PAN);
        /* calculate the relative degrees to move to the desired absolute pan-value */
        if( (res = value - pan) == 0 ) {
          /* do not move if this would mean to move by 0 degrees */
          res = pan/one_degree;
          break;
        }
        /* move it */
        pan = value;
        uvcPanTilt(videoIn->fd, res, 0, 0);
        res = pan/one_degree;
        DBG("pan: %d ", pan);
        break;
      case IN_CMD_PAN_PLUS:
        DBG("pan + ");
        if ( pan_tilt_valid != 1 ) {
          if ( input_cmd(IN_CMD_RESET_PAN_TILT_NO_MUTEX, 0) == -1 ) {
            res = -1;
            break;
          }
        }
        if ( (MAX_PAN) >= (pan+MIN_RES) ) {
          pan += MIN_RES;
          uvcPanTilt(videoIn->fd, MIN_RES, 0, 0);
        }
        res = pan/one_degree;
        DBG("pan: %d ", pan);
        break;
      case IN_CMD_PAN_MINUS:
        DBG("pan - ");
        if ( pan_tilt_valid != 1 ) {
          if ( input_cmd(IN_CMD_RESET_PAN_TILT_NO_MUTEX, 0) == -1 ) {
            res = -1;
            break;
          }
        }
        if ( (MIN_PAN) <= (pan-MIN_RES) ) {
          pan -= MIN_RES;
          uvcPanTilt(videoIn->fd, -MIN_RES, 0, 0);
        }
        res = pan/one_degree;
        DBG("pan: %d ", pan);
        break;
      case IN_CMD_TILT_SET:
        DBG("set tilt to %d degrees ", value);
        if ( pan_tilt_valid != 1 ) {
          if ( input_cmd(IN_CMD_RESET_PAN_TILT_NO_MUTEX, 0) == -1 ) {
            res = -1;
            break;
          }
        }
        /* limit pan-value to min and max, multiply it with constant "one_degree" */
        value = MIN(MAX(value*one_degree, MIN_TILT), MAX_TILT);
        /* calculate the relative degrees to move to the desired absolute pan-value */
        if( (res = value - tilt) == 0 ) {
          /* do not move if this would mean to move by 0 degrees */
          res = tilt/one_degree;
          break;
        }
        /* move it */
        tilt = value;
        uvcPanTilt(videoIn->fd, 0, res, 0);
        res = tilt/one_degree;
        DBG("tilt: %d ", tilt);
        break;
      case IN_CMD_TILT_PLUS:
        DBG("tilt + ");
        if ( pan_tilt_valid != 1 ) {
          if ( input_cmd(IN_CMD_RESET_PAN_TILT_NO_MUTEX, 0) == -1 ) {
            res = -1;
            break;
          }
        }
        if ( (MAX_TILT) >= (tilt+MIN_RES) ) {
          tilt += MIN_RES;
          uvcPanTilt(videoIn->fd, 0, MIN_RES, 0);
        }
        res = tilt/one_degree;
        DBG("tilt: %d ", tilt);
        break;
      case IN_CMD_TILT_MINUS:
        DBG("tilt - ");
        if ( pan_tilt_valid != 1 ) {
          if ( input_cmd(IN_CMD_RESET_PAN_TILT_NO_MUTEX, 0) == -1 ) {
            res = -1;
            break;
          }
        }
        if ( (MIN_TILT) <= (tilt-MIN_RES) ) {
          tilt -= MIN_RES;
          uvcPanTilt(videoIn->fd, 0, -MIN_RES, 0);
        }
        res = tilt/one_degree;
        DBG("tilt: %d ", tilt);
        break;
      case IN_CMD_SATURATION_PLUS:
        DBG("saturation + (%d) ", v4l2GetControl (videoIn, V4L2_CID_SATURATION));
        res = v4l2UpControl(videoIn, V4L2_CID_SATURATION);
        break;
      case IN_CMD_SATURATION_MINUS:
        DBG("saturation - (%d) ", v4l2GetControl (videoIn, V4L2_CID_SATURATION));
        res = v4l2DownControl(videoIn, V4L2_CID_SATURATION);
        break;
      case IN_CMD_CONTRAST_PLUS:
        DBG("contrast + (%d) ", v4l2GetControl (videoIn, V4L2_CID_CONTRAST));
        res = v4l2UpControl(videoIn, V4L2_CID_CONTRAST);
        break;
      case IN_CMD_CONTRAST_MINUS:
        DBG("contrast - (%d) ", v4l2GetControl (videoIn, V4L2_CID_CONTRAST));
        res = v4l2DownControl(videoIn, V4L2_CID_CONTRAST);
        break;
      case IN_CMD_BRIGHTNESS_PLUS:
        DBG("brightness + (%d) ", v4l2GetControl (videoIn, V4L2_CID_BRIGHTNESS));
        res = v4l2UpControl(videoIn, V4L2_CID_BRIGHTNESS);
        break;
      case IN_CMD_BRIGHTNESS_MINUS:
        DBG("brightness - (%d) ", v4l2GetControl (videoIn, V4L2_CID_BRIGHTNESS));
        res = v4l2DownControl(videoIn, V4L2_CID_BRIGHTNESS);
        break;
      case IN_CMD_GAIN_PLUS:
        DBG("gain + (%d) ", v4l2GetControl (videoIn, V4L2_CID_GAIN));
        res = v4l2UpControl(videoIn, V4L2_CID_GAIN);
        break;
      case IN_CMD_GAIN_MINUS:
        DBG("gain - (%d) ", v4l2GetControl (videoIn, V4L2_CID_GAIN));
        res = v4l2DownControl(videoIn, V4L2_CID_GAIN);
        break;
      case IN_CMD_FOCUS_PLUS:
        DBG("focus + (%d) ", focus);
        value=MIN(MAX(focus+10,0),255);
        if ( (res = v4l2SetControl(videoIn, V4L2_CID_FOCUS_LOGITECH, value)) == 0) {
          focus = value;
        }
        res = focus;
        break;
      case IN_CMD_FOCUS_MINUS:
        DBG("focus - (%d) ", focus);
        value=MIN(MAX(focus-10,0),255);
        if ( (res = v4l2SetControl(videoIn, V4L2_CID_FOCUS_LOGITECH, value)) == 0) {
          focus = value;
        }
        res = focus;
        break;
      case IN_CMD_FOCUS_SET:
        value=MIN(MAX(value,0),255);
        DBG("set focus to %d ", value);
        if ( (res = v4l2SetControl(videoIn, V4L2_CID_FOCUS_LOGITECH, value)) == 0) {
          focus = value;
        }
        res = focus;
        break;
      /* switch the webcam LED permanently on */
      case IN_CMD_LED_ON:
        res = v4l2SetControl(videoIn, V4L2_CID_LED1_MODE_LOGITECH, 1);
      break;
      /* switch the webcam LED permanently off */
      case IN_CMD_LED_OFF:
        res = v4l2SetControl(videoIn, V4L2_CID_LED1_MODE_LOGITECH, 0);
      break;
      /* switch the webcam LED on if streaming, off if not streaming */
      case IN_CMD_LED_AUTO:
        res = v4l2SetControl(videoIn, V4L2_CID_LED1_MODE_LOGITECH, 3);
      break;
      /* let the webcam LED blink at a given hardcoded intervall */
      case IN_CMD_LED_BLINK:
        res = v4l2SetControl(videoIn, V4L2_CID_LED1_MODE_LOGITECH, 2);
        res = v4l2SetControl(videoIn, V4L2_CID_LED1_FREQUENCY_LOGITECH, 255);
      break;
      default:
        DBG("nothing matched ");
        res = -1;
    } 
    if ( cmd != IN_CMD_RESET_PAN_TILT_NO_MUTEX )
      pthread_mutex_unlock( &controls_mutex );
    return res;
  }
  
 其中用到了v4l2ucv.c中的函数:
   int v4l2SetControl(struct vdIn *vd, int control, int value) {
     struct v4l2_control control_s;
     struct v4l2_queryctrl queryctrl;
     int min, max, step, val_def;
     int err;
   
     if (isv4l2Control(vd, control, &queryctrl) < 0)
       return -1;
   
     min = queryctrl.minimum;
     max = queryctrl.maximum;
     step = queryctrl.step;
     val_def = queryctrl.default_value;
   
     if ((value >= min) && (value <= max)) {
       control_s.id = control;
       control_s.value = value;
       if ((err = ioctl(vd->fd, VIDIOC_S_CTRL, &control_s)) < 0) {
         return -1;
       }
     }
   
     return 0;
   }
-----------------------------------------------------------------------------------------------------------------------
好,现在来分析input_run()函数:  哈哈,这个函数最简单~~~~~~~~~
 int input_run(void) {
   pglobal->buf = malloc(videoIn->framesizeIn);  /* 为帧缓存分配内存 */
   if (pglobal->buf == NULL) {
     fprintf(stderr, "could not allocate memory ");
     exit(EXIT_FAILURE);
   }
   pthread_create(&cam, 0, cam_thread, NULL);  /* 创建cam线程 */
   pthread_detach(cam);     /* 将线程与父线程分离 */
   return 0;
 }
-----------------------------------------------------------------------------------------------------------------------
cma_thread()函数中用到的几个函数:
 int uvcGrab(struct vdIn *vd)    /* 抓取函数 */
 {
 #define HEADERFRAME1 0xaf
   int ret;
   if (!vd->isstreaming)
     if (video_enable(vd))
       goto err;
   memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
   vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   vd->buf.memory = V4L2_MEMORY_MMAP;
   ret = ioctl(vd->fd, VIDIOC_DQBUF, &vd->buf);  /* 清除缓冲区的内容 */
   switch (vd->formatIn) {
     case V4L2_PIX_FMT_MJPEG:
       if (vd->buf.bytesused <= HEADERFRAME1) {     /* Prevent crashon empty image */
         fprintf(stderr, "Ignoring empty buffer ... ");
         return 0;
       }
       memcpy(vd->tmpbuffer, vd->mem[vd->buf.index], vd->buf.bytesused);
       if (debug)
         fprintf(stderr, "bytes in used %d ", vd->buf.bytesused);
       break;
     case V4L2_PIX_FMT_YUYV:
       if (vd->buf.bytesused > vd->framesizeIn)
         memcpy (vd->framebuffer, vd->mem[vd->buf.index], (size_t) vd->framesizeIn);
       else
         memcpy (vd->framebuffer, vd->mem[vd->buf.index], (size_t) vd->buf.bytesused);
       break;
     default:
       goto err;
     break;
   }
   ret = ioctl(vd->fd, VIDIOC_QBUF, &vd->buf);
   if (ret < 0) {
     perror("Unable to requeue buffer");
     goto err;
   }
   return 0;
 err:
   vd->signalquit = 0;
   return -1;
 }
 
 
 
 int memcpy_picture(unsigned char *out, unsigned char *buf, int size) /* 复制jpeg格式的图片到pglobal->buf */
 {
   unsigned char *ptdeb, *ptlimit, *ptcur = buf;
   int sizein, pos=0;
   if (!is_huffman(buf)) {
     ptdeb = ptcur = buf;
     ptlimit = buf + size;
     while ((((ptcur[0] << 8) | ptcur[1]) != 0xffc0) && (ptcur < ptlimit))
       ptcur++;
     if (ptcur >= ptlimit)
         return pos;
     sizein = ptcur - ptdeb;
     memcpy(out+pos, buf, sizein); pos += sizein;
     memcpy(out+pos, dht_data, sizeof(dht_data)); pos += sizeof(dht_data);
     memcpy(out+pos, ptcur, size - sizein); pos += size-sizein;
   } else {
     memcpy(out+pos, ptcur, size); pos += size;
   }
   return pos;
 }
-----------------------------------------------------------------------------------------------------------------------
现在分析一下cma_thread()函数,这个线程很重要哦!!!!!!该函数的作用是抓取一帧的图像,并复制到全局缓冲区
 void *cam_thread( void *arg ) {
   /* set cleanup handler to cleanup allocated ressources */
   p