无线视频监控Mjpg-Streamer之框架分析
来源:互联网 发布:隔墙听音器专卖店淘宝 编辑:程序博客网 时间:2024/05/22 12:48
1、框架
mjpg-streamer相当于一个码头,有仓库、输入与输出
2.分析源码
input_init();
output_init();
input_run();
output_run();
3.自己写客服端
(1).发送一个请求字符串
“GET /?action=snapshot\n”
“GET /?action=stream\n”
“GET /?action=command\n”
(2).再发送一次字符串
如果我们不使用密码功能!则只需发送任意长度为小于2字节的字符串,比如:
“f\n”
如果发送的请求是:”GET /?action=snapshot\n”
(3).需要接收一次字符串(是服务器发过来的报文)
(4).接收一帧图片
如果发送的请求是:”GET /?action=stream\n”
(3).需要接收一次字符串(是服务器发过来的报文)
while(1)
{
(4).再接收一次报文,解析它,得到一帧图片的大小(size)
(5).接收size个字节的数据
}
Mjpg_Steamer.c:
1、
采用dlopen、dlsym、dlclose加载动态链接库【总结】
具体参考:http://www.cnblogs.com/Anker/p/3746802.html
dlopen以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程,
dlerror返回出现的错误,
dlsym通过句柄和连接符名称获取函数名或者变量名,
dlclose来卸载打开的库。 dlopen打开模式如下:
2、源码分析:
/*******************************************************************************# ## MJPG-streamer allows to stream JPG frames from an input-plugin ## to several output plugins ## ## Copyright (C) 2007 Tom St枚veken ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## #*******************************************************************************/#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <linux/videodev.h>#include <sys/ioctl.h>#include <errno.h>#include <signal.h>#include <sys/socket.h>#include <arpa/inet.h>#include <sys/types.h>#include <sys/stat.h>#include <getopt.h>#include <pthread.h>#include <dlfcn.h>#include <fcntl.h>#include <syslog.h>#include "utils.h"#include "mjpg_streamer.h"/* globals */static globals global;/******************************************************************************Description.: Display a help messageInput Value.: argv[0] is the program name and the parameter prognameReturn Value: -******************************************************************************/void help(char *progname){ fprintf(stderr, "-----------------------------------------------------------------------\n"); fprintf(stderr, "Usage: %s\n" \ " -i | --input \"<input-plugin.so> [parameters]\"\n" \ " -o | --output \"<output-plugin.so> [parameters]\"\n" \ " [-h | --help ]........: display this help\n" \ " [-v | --version ].....: display version information\n" \ " [-b | --background]...: fork to the background, daemon mode\n", progname); fprintf(stderr, "-----------------------------------------------------------------------\n"); fprintf(stderr, "Example #1:\n" \ " To open an UVC webcam \"/dev/video1\" and stream it via HTTP:\n" \ " %s -i \"input_uvc.so -d /dev/video1\" -o \"output_http.so\"\n", progname); fprintf(stderr, "-----------------------------------------------------------------------\n"); fprintf(stderr, "Example #2:\n" \ " To open an UVC webcam and stream via HTTP port 8090:\n" \ " %s -i \"input_uvc.so\" -o \"output_http.so -p 8090\"\n", progname); fprintf(stderr, "-----------------------------------------------------------------------\n"); fprintf(stderr, "Example #3:\n" \ " To get help for a certain input plugin:\n" \ " %s -i \"input_uvc.so --help\"\n", progname); fprintf(stderr, "-----------------------------------------------------------------------\n"); fprintf(stderr, "In case the modules (=plugins) can not be found:\n" \ " * Set the default search path for the modules with:\n" \ " export LD_LIBRARY_PATH=/path/to/plugins,\n" \ " * or put the plugins into the \"/lib/\" or \"/usr/lib\" folder,\n" \ " * or instead of just providing the plugin file name, use a complete\n" \ " path and filename:\n" \ " %s -i \"/path/to/modules/input_uvc.so\"\n", progname); fprintf(stderr, "-----------------------------------------------------------------------\n");}/******************************************************************************Description.: pressing CTRL+C sends signals to this process instead of just killing it plugins can tidily shutdown and free allocated ressources. The function prototype is defined by the system, because it is a callback function.Input Value.: sig tells us which signal was receivedReturn Value: -******************************************************************************/void signal_handler(int sig){ int i; /* signal "stop" to threads */ LOG("setting signal to stop\n"); global.stop = 1; usleep(1000*1000); /* clean up threads */ LOG("force cancelation of threads and cleanup ressources\n"); global.in.stop(); for(i=0; i<global.outcnt; i++) { global.out[i].stop(global.out[i].param.id); } usleep(1000*1000); /* close handles of input plugins */ dlclose(&global.in.handle); for(i=0; i<global.outcnt; i++) { /* skip = 0; DBG("about to decrement usage counter for handle of %s, id #%02d, handle: %p\n", \ global.out[i].plugin, global.out[i].param.id, global.out[i].handle); for(j=i+1; j<global.outcnt; j++) { if ( global.out[i].handle == global.out[j].handle ) { DBG("handles are pointing to the same destination (%p == %p)\n", global.out[i].handle, global.out[j].handle); skip = 1; } } if ( skip ) { continue; } DBG("closing handle %p\n", global.out[i].handle); */ dlclose(global.out[i].handle); } DBG("all plugin handles closed\n"); pthread_cond_destroy(&global.db_update); pthread_mutex_destroy(&global.db); LOG("done\n"); closelog(); exit(0); return;}/******************************************************************************Description.:Input Value.:Return Value:******************************************************************************/int main(int argc, char *argv[]){ char *input = "input_uvc.so --resolution 640x480 --fps 5 --device /dev/video0"; // 指向输入选项的参数字符串 char *output[MAX_OUTPUT_PLUGINS]; /* 指向输出选项的参数字符串 */ int daemon=0, i; /* 是否让程序在后台运行的标志 */ size_t tmp=0; output[0] = "output_http.so --port 8080"; global.outcnt = 0; /* 此时输出通道有几种方式, 0 */ /* parameter parsing */ /* 解析参数(关键是看懂 getopt_long_only 函数) */ /* getopt_long_only(): 用于解析命令行选项 参数解析: 第二个参数:直接从main函数传递而来,表示我们传入的参数 第三个参数:短选项字符串 第四个参数:是 struct option 数组,用于存放长选项参数 第五个参数:用于返回长选项在longopts结构体数组中的索引值 返回值: 解析完毕,返回-1 出现未定义的长选项或者短选项,getopt_long返回? 注意: 长选项必须加'-' */ 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} }; /* argv = " -i "input_uvc.so -f 10 -r 320*240" -o "output_http.so -w www" " */ 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 = strdup(optarg); // input = "input_uvc.so -f 10 -r 320*240" break; /* o, output */ case 4: case 5: output[global.outcnt++] = strdup(optarg); // output[0] = "output_http.so -w www" break; /* v, version */ case 6: case 7: printf("MJPG Streamer Version: %s\n" \ "Compilation Date.....: %s\n" \ "Compilation Time.....: %s\n", SOURCE_VERSION, __DATE__, __TIME__); return 0; break; /* b, background */ case 8: case 9: daemon=1; break; default: help(argv[0]); return 0; } } /* 打开一个程序的系统记录器的链接 */ openlog("MJPG-streamer ", LOG_PID|LOG_CONS, LOG_USER); //openlog("MJPG-streamer ", LOG_PID|LOG_CONS|LOG_PERROR, LOG_USER); syslog(LOG_INFO, "starting application"); // 将 "starting application" 字符串写到系统记录中 /* fork to the background */ /* 如果daemon = 1,则让程序在后台运行 */ if ( daemon ) { LOG("enabling daemon mode"); daemon_mode(); } /* initialise the global variables */ /* 初始化 global 中的成员 */ global.stop = 0; global.buf = NULL; global.size = 0; global.in.plugin = NULL; /* this mutex and the conditional variable are used to synchronize access to the global picture buffer */ if( pthread_mutex_init(&global.db, NULL) != 0 ) /* 初始化 global.db 成员 */ { LOG("could not initialize mutex variable\n"); closelog(); exit(EXIT_FAILURE); } if( pthread_cond_init(&global.db_update, NULL) != 0 ) /* 初始化 global.db_update(条件变量) 成员 */ { LOG("could not initialize condition variable\n"); closelog(); exit(EXIT_FAILURE); } /* ignore SIGPIPE (send by OS if transmitting to closed TCP sockets) */ signal(SIGPIPE, SIG_IGN); /* 忽略 SIGPIPE 信号 */ /* register signal handler for <CTRL>+C in order to clean up */ /* 当我们按下 <CTRL>+C 时,则调用signal_handler()函数,做一些清理工作 */ if (signal(SIGINT, signal_handler) == SIG_ERR) { LOG("could not register signal handler\n"); closelog(); exit(EXIT_FAILURE); } /* * messages like the following will only be visible on your terminal * if not running in daemon mode */ LOG("MJPG Streamer Version.: %s\n", SOURCE_VERSION); // 打印出 MJPG Streamer 版本号 /* check if at least one output plugin was selected */ if ( global.outcnt == 0 ) // 如果输出方式的种类为0,则让他为1 { /* no? Then use the default plugin instead */ global.outcnt = 1; } /* open input plugin */ /* "input_uvc.so -f 10 -r 320*240" */ tmp = (size_t)(strchr(input, ' ')-input); // 让tmp 等于 "input_uvc.so"字符串的长度 global.in.plugin = (tmp > 0)?strndup(input, tmp):strdup(input); // global.in.plugin = "input_uvc.so" global.in.handle = dlopen(global.in.plugin, RTLD_LAZY); // 打开 "input_uvc.so" 这个动态链接库 if ( !global.in.handle ) { LOG("ERROR: could not find input plugin\n"); LOG(" Perhaps you want to adjust the search path with:\n"); LOG(" # export LD_LIBRARY_PATH=/path/to/plugin/folder\n"); LOG(" dlopen: %s\n", dlerror() ); closelog(); exit(EXIT_FAILURE); } global.in.init = dlsym(global.in.handle, "input_init"); // 让 global.in.init = input_init if ( global.in.init == NULL ) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); } global.in.stop = dlsym(global.in.handle, "input_stop"); // 让 global.in.stop = input_stop if ( global.in.stop == NULL ) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); } global.in.run = dlsym(global.in.handle, "input_run"); // 让 global.in.run = input_run if ( global.in.run == NULL ) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); } /* try to find optional command */ global.in.cmd = dlsym(global.in.handle, "input_cmd"); // global.in.cmd = input_cmd /*Garmen:strchr() 将会找出 str 字符串中第一次出现的字符 c 的地址,然后将该地址返回。*/ global.in.param.parameter_string = strchr(input, ' '); // global.in.param.parameter_string = "-f 10 -r 320*240" //原来字符串:input = "input_uvc.so -f 10 -r 320*240" global.in.param.global = &global; if ( global.in.init(&global.in.param) ) // 调用input_uvc.c中的input_init(&global.in.param)函数 { LOG("input_init() return value signals to exit"); closelog(); exit(0); } /* open output plugin */ for (i=0; i<global.outcnt; i++) // outcnt = 1 { /* "output_http.so -w www" */ tmp = (size_t)(strchr(output[i], ' ')-output[i]); // 让tmp 等于 "output_http.so"字符串的长度 global.out[i].plugin = (tmp > 0)?strndup(output[i], tmp):strdup(output[i]); // 让 global.out[i].plugin = "output_http.so" global.out[i].handle = dlopen(global.out[i].plugin, RTLD_LAZY); // 打开 "output_http.so" 动态链接库 if ( !global.out[i].handle ) { LOG("ERROR: could not find output plugin %s\n", global.out[i].plugin); LOG(" Perhaps you want to adjust the search path with:\n"); LOG(" # export LD_LIBRARY_PATH=/path/to/plugin/folder\n"); LOG(" dlopen: %s\n", dlerror() ); closelog(); exit(EXIT_FAILURE); } global.out[i].init = dlsym(global.out[i].handle, "output_init"); // 让 global.out[i].init = output_init if ( global.out[i].init == NULL ) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); } global.out[i].stop = dlsym(global.out[i].handle, "output_stop"); // 让 global.out[i].stop = output_stop if ( global.out[i].stop == NULL ) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); } global.out[i].run = dlsym(global.out[i].handle, "output_run"); // 让 global.out[i].run = output_run if ( global.out[i].run == NULL ) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); } /* try to find optional command */ global.out[i].cmd = dlsym(global.out[i].handle, "output_cmd"); // 让 global.out[i].cmd = output_cmd global.out[i].param.parameter_string = strchr(output[i], ' '); // 让 global.out[i].param.parameter_string = "-w www" global.out[i].param.global = &global; global.out[i].param.id = i; if ( global.out[i].init(&global.out[i].param) ) // 调用 output_http.c中的output_init(&global.out[i].param)函数 { LOG("output_init() return value signals to exit"); closelog(); exit(0); } } /* start to read the input, push pictures into global buffer */ DBG("starting input plugin\n"); // 打印调试信息 "starting input plugin\n" syslog(LOG_INFO, "starting input plugin"); // 将 "starting input plugin" 字符串写到记录本中 global.in.run(); // 调用 input_uvc.c中的input_run函数 DBG("starting %d output plugin(s)\n", global.outcnt); // 打印调试信息 for(i=0; i<global.outcnt; i++) { syslog(LOG_INFO, "starting output plugin: %s (ID: %02d)", global.out[i].plugin, global.out[i].param.id); global.out[i].run(global.out[i].param.id); // 调用 output_http.c 中的 output_run 函数 } /* wait for signals */ pause(); // 程序等待信号的发生 return 0;}
- 无线视频监控Mjpg-Streamer之框架分析
- 无线视频监控Mjpg-streamer输入分析
- 无线视频监控Mjpg-streamer输出分析
- 远程视频监控之应用篇(mjpg-streamer)
- 远程视频监控之应用篇(mjpg-streamer)
- 树莓派安装mjpg-streamer视频监控
- 树莓派上安装MJPG-streamer实现视频监控
- mjpg-streamer学习笔记2----框架分析
- 基于mjpg-streamer远程视频WEB监控 项目笔记一
- 基于mjpg-streamer远程视频WEB监控 项目笔记二
- 基于mjpg-streamer远程视频WEB监控 项目笔记一
- 基于mjpg-streamer远程视频WEB监控 项目笔记二
- 基于mjpg-streamer远程视频WEB监控 项目手札
- Android + Mini2440 无线网络视频监控系统 mjpg-streamer的移植
- 基于mjpg-streamer远程视频WEB监控 项目笔记一
- 基于mjpg-streamer远程视频WEB监控 项目笔记二
- mjpg-streamer源码分析
- mjpg-streamer源码分析
- c++标准库
- 查杀8080端口占用
- 安卓自定义View进阶-分类与流程
- 计算机组成原理(2)复习提纲
- 【脚本语言系列】关于Python基础知识处理字符串,你需要知道的事
- 无线视频监控Mjpg-Streamer之框架分析
- 重入锁
- 算法导论 练习题 16.2-6
- 【数据结构】尾插法建立单链表 以及删除操作
- Tensorflow 入门
- win10下安装ubuntu16.04双系统
- POJ 2104 K-th number 主席树 函数式线段树
- hibernate入门之oracle连接
- 史上最难的一道Java面试题