转载于http://blog.csdn.net/tandesir/article/details/8437272
【问题描述】在友善之臂视频监控方案源码学习(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.csdn.net/detail/tandesir/4915905
转载请标明出处,仅供学习交流,勿用于商业目的
Copyright @ http://blog.csdn.net/tandesir