分享

Mjpeg-streamer源码分析(二)

 champion_xu 2012-05-02

                                         input_uvc.c源码分析
-----------------------------------------------------------------------------------------------------------------------
重要函数解析:
 char *strtok_r(char *str, const char *delim, char **saveptr);
 
与线程相关的函数:
 线程可以安排它推出时需要调用的函数,这与进程可以用atexit函数安排进程退出时需要调用的函数是类似的.
 这样的函数称为线程清理处理程序,线程可以建立多个清理处理程序.处理程序记录在栈内,也就是说它们的调
 用顺序与它们的注册顺序相反
 #include <pthread.h>
 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\n", dev);
  IPRINT("Desired Resolution: %i x %i\n", width, height);
  IPRINT("Frames Per Second.: %i\n", fps);
  IPRINT("Format............: %s\n", (format==V4L2_PIX_FMT_YUYV)?"YUV":"MJPEG");
  if ( format == V4L2_PIX_FMT_YUYV )
   IPRINT("JPEG Quality......: %d\n", 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 \n");
        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 !!\n");
        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\n");
        break;
      case IN_CMD_RESET:
        DBG("about to reset all image controls to defaults\n");
        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\n");
        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\n", 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\n", pan);
        break;
      case IN_CMD_PAN_PLUS:
        DBG("pan +\n");
        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\n", pan);
        break;
      case IN_CMD_PAN_MINUS:
        DBG("pan -\n");
        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\n", pan);
        break;
      case IN_CMD_TILT_SET:
        DBG("set tilt to %d degrees\n", 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\n", tilt);
        break;
      case IN_CMD_TILT_PLUS:
        DBG("tilt +\n");
        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\n", tilt);
        break;
      case IN_CMD_TILT_MINUS:
        DBG("tilt -\n");
        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\n", tilt);
        break;
      case IN_CMD_SATURATION_PLUS:
        DBG("saturation + (%d)\n", v4l2GetControl (videoIn, V4L2_CID_SATURATION));
        res = v4l2UpControl(videoIn, V4L2_CID_SATURATION);
        break;
      case IN_CMD_SATURATION_MINUS:
        DBG("saturation - (%d)\n", v4l2GetControl (videoIn, V4L2_CID_SATURATION));
        res = v4l2DownControl(videoIn, V4L2_CID_SATURATION);
        break;
      case IN_CMD_CONTRAST_PLUS:
        DBG("contrast + (%d)\n", v4l2GetControl (videoIn, V4L2_CID_CONTRAST));
        res = v4l2UpControl(videoIn, V4L2_CID_CONTRAST);
        break;
      case IN_CMD_CONTRAST_MINUS:
        DBG("contrast - (%d)\n", v4l2GetControl (videoIn, V4L2_CID_CONTRAST));
        res = v4l2DownControl(videoIn, V4L2_CID_CONTRAST);
        break;
      case IN_CMD_BRIGHTNESS_PLUS:
        DBG("brightness + (%d)\n", v4l2GetControl (videoIn, V4L2_CID_BRIGHTNESS));
        res = v4l2UpControl(videoIn, V4L2_CID_BRIGHTNESS);
        break;
      case IN_CMD_BRIGHTNESS_MINUS:
        DBG("brightness - (%d)\n", v4l2GetControl (videoIn, V4L2_CID_BRIGHTNESS));
        res = v4l2DownControl(videoIn, V4L2_CID_BRIGHTNESS);
        break;
      case IN_CMD_GAIN_PLUS:
        DBG("gain + (%d)\n", v4l2GetControl (videoIn, V4L2_CID_GAIN));
        res = v4l2UpControl(videoIn, V4L2_CID_GAIN);
        break;
      case IN_CMD_GAIN_MINUS:
        DBG("gain - (%d)\n", v4l2GetControl (videoIn, V4L2_CID_GAIN));
        res = v4l2DownControl(videoIn, V4L2_CID_GAIN);
        break;
      case IN_CMD_FOCUS_PLUS:
        DBG("focus + (%d)\n", 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)\n", 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\n", 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\n");
        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\n");
     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 ...\n");
         return 0;
       }
       memcpy(vd->tmpbuffer, vd->mem[vd->buf.index], vd->buf.bytesused);
       if (debug)
         fprintf(stderr, "bytes in used %d \n", 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 */
   pthread_cleanup_push(cam_cleanup, NULL);  /* 注册清除处理函数cam_cleanup() */
   while( !pglobal->stop ) {
     /* grab a frame */
     if( uvcGrab(videoIn) < 0 ) {   /* 在videoIn结构体中抓取一个帧图像 */
       IPRINT("Error grabbing frames\n");
       exit(EXIT_FAILURE);
     }
     DBG("received frame of size: %d\n", videoIn->buf.bytesused);
     /*
      * Workaround for broken, corrupted frames:
      * Under low light conditions corrupted frames may get captured.
      * The good thing is such frames are quite small compared to the regular pictures.
      * For example a VGA (640x480) webcam picture is normally >= 8kByte large,
      * corrupted frames are smaller.
      */
     if ( videoIn->buf.bytesused < minimum_size ) {
       DBG("dropping too small frame, assuming it as broken\n");
       continue;
     }
     /* copy JPG picture to global buffer */
     pthread_mutex_lock( &pglobal->db );
     /*
      * If capturing in YUV mode convert to JPEG now.
      * This compression requires many CPU cycles, so try to avoid YUV format.
      * Getting JPEGs straight from the webcam, is one of the major advantages of
      * Linux-UVC compatible devices.
      */
     if (videoIn->formatIn == V4L2_PIX_FMT_YUYV) {
       DBG("compressing frame\n");
       pglobal->size = compress_yuyv_to_jpeg(videoIn, pglobal->buf, videoIn->framesizeIn, gquality);
     }
     else {
       DBG("copying frame\n");
       pglobal->size = memcpy_picture(pglobal->buf, videoIn->tmpbuffer, videoIn->buf.bytesused);
     }
 #if 0
     /* motion detection can be done just by comparing the picture size, but it is not very accurate!! */
     if ( (prev_size - global->size)*(prev_size - global->size) > 4*1024*1024 ) {
         DBG("motion detected (delta: %d kB)\n", (prev_size - global->size) / 1024);
     }
     prev_size = global->size;
 #endif
     /* signal fresh_frame */
     pthread_cond_broadcast(&pglobal->db_update);
     pthread_mutex_unlock( &pglobal->db );
     DBG("waiting for next frame\n");
     /* only use usleep if the fps is below 5, otherwise the overhead is too long */
     if ( videoIn->fps < 5 ) {
       usleep(1000*1000/videoIn->fps);
     }
   }
   DBG("leaving input thread, calling cleanup function now\n");
   pthread_cleanup_pop(1);
   return NULL;
 }
-----------------------------------------------------------------------------------------------------------------------

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多