【问题描述】在友善之臂视频监控方案源码学习(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