友善之臂视频监控方案源码学习(5) - 输入控制

本文详细解析了友善之臂视频监控方案的输入控制部分,包括涉及的文件、输入结构、input_init、input_run、input_stop和input_cmd函数的功能,以及参数解析和视频设备的打开。主要关注input_init的参数解析、videoIn结构的构建和视频设备的初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【问题描述】在友善之臂视频监控方案源码学习(4) - 数据流向一文中,对视频数据流向进行了简要阐述。本文对输入控制进行解析。

【解析】

1 涉及到的文件和目录

mjpg-streamer-mini2440-read-only/start_uvc.sh

mjpg-streamer-mini2440-read-only/mjpg_streamer.c

mjpg-streamer-mini2440-read-only/mjpg_streamer.h

mjpg-streamer-mini2440-read-only/plugins/input.h

mjpg-streamer-mini2440-read-only/plugins/input_uvc

 

2 输入结构

mjpg-streamer-mini2440-read-only/plugins目录下input.h中对input结构描述如下:

/* structure to store variables/functions for input plugin */
typedef struct _input input;
struct _input {
  char *plugin;
  void *handle;
  input_parameter param;

  int (*init)(input_parameter *);
  int (*stop)(void);
  int (*run)(void);
  int (*cmd)(in_cmd_type, int);
};

友善之臂视频监控方案源码学习(1) - 架构分析一文,指出了该方案实质上就是实现了输入、输出的接口。从输入看,就是实现了init、stop、run、cmd函数指针。主程序中实际上,只调用了init、run接口。stop接口是在信号的回调函数void signal_handler(int sig);中调用的。

 

3 input_init分析

(1) 定义在mjpg-streamer-mini2440-read-only/plugins/input_uvc/Input_uvc.c文件中

(2) 在mjpg-streamer-mini2440-read-only/mjpg_streamer.c 的main函数中,默认的输入为:

char *input  = "input_uvc.so --resolution 640x480 --fps 5 --device /dev/video0";

若-i参数不为空,则采用下述方法更新输入:

/* i, input */
      case 2:
      case 3:
        input = strdup(optarg);
        break;

传送给Input_uvc.c中input_init的参数为:

global.in.param.parameter_string = strchr(input, ' ');

下面分析mjpg-streamer-mini2440-read-only/plugins/input_uvc/input_uvc.c中的input_init接口。接口定义如下:

int input_init(input_parameter *param);

首先,定义了一系列默认的参数:

char *argv[MAX_ARGUMENTS]={NULL}, *dev = "/dev/video0", *s;
int argc=1, width=640, height=480, fps=5, format=V4L2_PIX_FMT_MJPEG, i;
in_cmd_type led = IN_CMD_LED_AUTO;
char fourcc[5]={0,0,0,0,0};

第二,初始化互斥锁:

 /* initialize the mutes variable */
  if( pthread_mutex_init(&controls_mutex, NULL) != 0 ) {
    IPRINT("could not initialize mutex variable\n");
    exit(EXIT_FAILURE);
  }

第三,参数解析。参数解析又分为下面几个步骤:

(a) 读取参数

argv[0] = INPUT_PLUGIN_NAME;
  if ( param->parameter_string != NULL && strlen(param->parameter_string) != 0 ) {
    char *arg=NULL, *saveptr=NULL, *token=NULL;

    arg=(char *)strdup(param->parameter_string);

(b) 将字符串形式的参数分解为字符串数组

if ( strchr(arg, ' ') != NULL ) {
      token=strtok_r(arg, " ", &saveptr);
      if ( token != NULL ) {
        argv[argc] = strdup(token);
        argc++;
        while ( (token=strtok_r(NULL, " ", &saveptr)) != NULL ) {
          argv[argc] = strdup(token);
          argc++;
          if (argc >= MAX_ARGUMENTS) {
            IPRINT("ERROR: too many arguments to input plugin\n");
            return 1;
          }
        }
      }
    }
  }

(c) 利用getopt函数解析参数

reset_getopt();
  while(1) {
    int option_index = 0, c=0;
    static struct option long_options[] = \
    {
      {"h", no_argument, 0, 0},
      {"help", no_argument, 0, 0},
      {"d", required_argument, 0, 0},
      {"device", required_argument, 0, 0},
      {"r", required_argument, 0, 0},
      {"resolution", required_argument, 0, 0},
      {"f", required_argument, 0, 0},
      {"fps", required_argument, 0, 0},
      {"y", no_argument, 0, 0},
      {"yuv", no_argument, 0, 0},
      {"q", required_argument, 0, 0},
      {"quality", required_argument, 0, 0},
      {"m", required_argument, 0, 0},
      {"minimum_size", required_argument, 0, 0},
      {"n", no_argument, 0, 0},
      {"no_dynctrl", no_argument, 0, 0},
      {"l", required_argument, 0, 0},
      {"led", required_argument, 0, 0},
      {0, 0, 0, 0}
    };

    /* parsing all parameters according to the list above is sufficent */
    c = getopt_long_only(argc, argv, "", long_options, &option_index);

该过程详细请参考友善之臂视频监控方案源码学习(2) - 主程序实现细节一文描述。

(d) 根据输入的参数执行相应的操作:

/* no more options to parse */
    if (c == -1) break;

    /* unrecognized option */
    if (c == '?'){
      help();
      return 1;
    }

    /* dispatch the given options */
    switch (option_index) {
      /* h, help */
      case 0:
      case 1:
        DBG("case 0,1\n");
        help();
        return 1;
        break;

      /* d, device */
      case 2:
      case 3:
        DBG("case 2,3\n");
        dev = strdup(optarg);
        break;

      /* r, resolution */
      case 4:
      case 5:
        DBG("case 4,5\n");
        width = -1;
        height = -1;

        /* try to find the resolution in lookup table "resolutions" */
        for ( i=0; i < LENGTH_OF(resolutions); i++ ) {
          if ( strcmp(resolutions[i].string, optarg) == 0 ) {
            width  = resolutions[i].width;
            height = resolutions[i].height;
          }
        }
        /* done if width and height were set */
        if(width != -1 && height != -1)
          break;
        /* parse value as decimal value */
        width  = strtol(optarg, &s, 10);
        height = strtol(s+1, NULL, 10);
        break;

      /* f, fps */
      case 6:
      case 7:
        DBG("case 6,7\n");
        fps=atoi(optarg);
        break;

      /* y, yuv */
      case 8:
      case 9:
        DBG("case 8,9\n");
        format = V4L2_PIX_FMT_YUYV;
        break;

      /* q, quality */
      case 10:
      case 11:
        DBG("case 10,11\n");
        format = V4L2_PIX_FMT_YUYV;
        gquality = MIN(MAX(atoi(optarg), 0), 100);
        break;

      /* m, minimum_size */
      case 12:
      case 13:
        DBG("case 12,13\n");
        minimum_size = MAX(atoi(optarg), 0);
        break;

      /* n, no_dynctrl */
      case 14:
      case 15:
        DBG("case 14,15\n");
        dynctrls = 0;
        break;

      /* l, led */
      case 16:
      case 17:
        DBG("case 16,17\n");
        if ( strcmp("on", optarg) == 0 ) {
          led = IN_CMD_LED_ON;
        } else if ( strcmp("off", optarg) == 0 ) {
          led = IN_CMD_LED_OFF;
        } else if ( strcmp("auto", optarg) == 0 ) {
          led = IN_CMD_LED_AUTO;
        } else if ( strcmp("blink", optarg) == 0 ) {
          led = IN_CMD_LED_BLINK;
        }
        break;

      default:
        DBG("default case\n");
        help();
        return 1;
    }

注:步骤(c)和(d)是在while(1)循环内检测的。

第四,使全局指针指向param->param->global

/* keep a pointer to the global variables */
  pglobal = param->global;

这一步非常重要,视频数据信息就存储在global结构的buf变量中。

第五,构建videoIn结构

videoIn = malloc(sizeof(struct vdIn));
  if ( videoIn == NULL ) {
    IPRINT("not enough memory for videoIn\n");
    exit(EXIT_FAILURE);
  }
  memset(videoIn, 0, sizeof(struct vdIn));

该结构描述如下:

struct vdIn {
	int fd;
	char *videodevice ;
	unsigned char *pFramebuffer;
	unsigned char *ptframe[OUTFRMNUMB];
  unsigned char *mem[NB_BUFFER];
	
	int framelock[OUTFRMNUMB];
	pthread_mutex_t grabmutex;
	int          framesizeIn ;
	volatile int frame_cour;
	int  bppIn;
	int  hdrwidth;
	int  hdrheight;
	int  formatIn;
	int  signalquit;	
	struct v4l2_capability cap;
	struct v4l2_format fmt;
	struct v4l2_buffer buf;
	struct v4l2_requestbuffers rb;
	
  int  grayscale;
	uint32_t quality;
	
	};

主要定义了视频输入控制变量。

第六,打开视频设备

/* open video device and prepare data structure */
  if (init_videoIn(videoIn, dev, width, height, fps, format, 1) < 0) {
    IPRINT("init_VideoIn failed\n");
    closelog();
    exit(EXIT_FAILURE);
  }

init_videoIn具体实现如下:

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));
  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:
  default:
    vd->framebuffer =
        (unsigned char *) calloc(1, (size_t) vd->framesizeIn);
    break;
    //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;
}

主要是完成了vdIn结构的初始化操作。

第七,动态控制初始化

if (dynctrls)
    initDynCtrls(videoIn->fd);

第八,LED初始化

in_cmd_type led = IN_CMD_LED_AUTO;
...
/*
   * switch the LED according to the command line parameters (if any)
   */
  input_cmd(led, 0);
 

其执行的命令定义在input_cmd函数中:

case IN_CMD_LED_AUTO:
      res = v4l2SetControl(videoIn, V4L2_CID_LED1_MODE_LOGITECH, 3);
    break;

 

4 input_run分析

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);
  pthread_detach(cam);

  return 0;
}

input_run只做了两件事:

(1) 分配视频数据存储空间

(2) 开辟视频采集线程。后续文章详细分析。

 

5 input_stop分析

input_stop主要功能是关闭视频采集线程

int input_stop(void) {
  DBG("will cancel input thread\n");
  pthread_cancel(cam);

  return 0;
}

 

6 input_cmd分析

该函数完成了视频输入的命令控制。在后续文章中将进行详细分析。

 


 【源码下载】

http://download.youkuaiyun.com/detail/tandesir/4915905

 

 

转载请标明出处,仅供学习交流,勿用于商业目的

Copyright @ http://blog.youkuaiyun.com/tandesir



 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值