源码阅读顺序
mjpg_streamer/mjpg_streamer.c
->int main(int argc, char argv[])
mjpg_streamer/plugins/input_uvc.c
->input_init()
->intput_run()
*->cam_thread()
mjpg-streamer\plugins\output_http.c
->output_init()
->output_run()
->*server_thread()
->*client_thread()
源码结构分析流程图
这里借用一下韦东山老师画的流程图
从这张流程图中,我们可以看出 *.so 的文件一共有7个 ,分别为三个输入插件(动态库):视频从文件读入(input_file);视频从摄像头读入(input_uvc);视频从网页读入(input_http)。 四个输出插件:视频输出到文件(output_file);视频输出到网页(output_http)。以input_uvc.so为例,通过dlopen()函数,调用该动态库的函数input_init() 完成摄像头的初始化,再创建一条线程,该线程负责完成视频流的读入,转化,导入到缓存区。 以output_http.so 为例 主进程main会通过dlopen,调用到该动态库的函数output_init来处理用户传入的参数,之后进入循环,等待input线程的请求连接,为每一对确认连接创建一个线程,该线程会读取缓存区中的视频流,然后将其输入到浏览器。
mjpg_streamer.c源码分析
下面我们来看看mjpg_streamer.c中主进程的源码
//为了不使篇幅过长,进行了一些删减
int main(int argc, char *argv[])
{
//char *input = "input_uvc.so --resolution 640x480 --fps 5 --device /dev/video0";
char *input[MAX_INPUT_PLUGINS];
char *output[MAX_OUTPUT_PLUGINS];
int daemon = 0, i;
size_t tmp = 0;
output[0] = "output_http.so --port 8080";//定义默认输出的插件和端口
global.outcnt = 0;
/* parameter parsing */
while(1) {
int option_index = 0, c = 0;
static struct option long_options[] = {
{
"h", no_argument, 0, 0
},
{
"help", no_argument, 0, 0},
{
"i", required_argument, 0, 0},
{
"input", required_argument, 0, 0},
{
"o", required_argument, 0, 0},
{
"output", required_argument, 0, 0},
{
"v", no_argument, 0, 0},
{
"version", no_argument, 0, 0},
{
"b", no_argument, 0, 0},
{
"background", no_argument, 0, 0},
{
0, 0, 0, 0}
};
/*
getopt_long_only():
用于解析命令行选项
根据传入的参数,一个一个的在struct option 数组里面进行匹配,当匹配到相同的参数,返回在数组中的下标。如传入-i选项 ,argv中自然有i选项,就会和struct option 数组项进行匹配,找到时返回数组下标。option_index索引值为2.
*/
c = getopt_long_only(argc, argv, "", long_options, &option_index);
/* no more options to parse */
if(c == -1) break;
/* unrecognized option */
if(c == '?') {
help(argv[0]);
return 0;
}
switch(option_index) {
/* h, help */
case 0:
case 1:
help(argv[0]);
return 0;
break;
/* i, input */
case 2:
case 3:
input[global.incnt++] = strdup(optarg);/*在getopt_long_only函数中,把传入的-i选项对应的字符串"input_uvc.so -f 10 -r 320*240"赋给变量optarg,
而strdup()函数用于将变量optarg的值赋予指针input */
break;
/* o, output */ //这一块就是把数据流的出入流和输出流给定义,如果执行时,用户未指定则采用默认配置->开头定义处
case 4:
case 5:
output[global.outcnt++] = strdup(optarg);/*在getopt_long_only函数中,把传入的-o选项对应的字符串"output_uvc.so ......"赋给变量optarg,
而strdup()函数用于将变量optarg的值赋予指针output */
break;
/* v, version */
case 6:
case 7:
printf("MJPG Streamer Version: %s\n" \
"Compilation Date.....: %s\n" \
"Compilation Time.....: %s\n",
#ifdef SVN_REV
SVN_REV,
#else
SOURCE_VERSION,