友善之臂视频监控方案源码学习(5) - 输入控制
【问题描述】在/* 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);};
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, ' ');
下面分析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);
该过程详细请参考/* 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