zoukankan      html  css  js  c++  java
  • Mjpegstreamer源码分析(二)

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

  • 相关阅读:
    NGINX 代理以及 HTTPS (一)
    HTTP 各种特性应用(二)
    HTTP 各种特性应用(一)
    HTTP 协议基础及发展历史
    添加 表格
    C# 利用反射和特性 来做一些事情
    HTTP 与 HTTPS
    系统登录详解
    js表单提交到后台对象接收
    idea插件
  • 原文地址:https://www.cnblogs.com/shulianghe/p/3724150.html
Copyright © 2011-2022 走看看