项目概述:
Mjpg-streamer之前大家或许有了解过,它可以从单一组件获取图像并传输到多个输出组件的命令行式的应用程序,将JPEG的文件视频流化并通过互联网将视频流从这里传送到web浏览器上。这将使得在公司或是外地,通过访问家里安装的摄像头查看家里的情况,实现室内监控。这里正是基于此场景借助Dragonboard来实现下局域网内家庭监控。
硬件准备:
- Dragonboard 410 *1
- PC机*1
- 摄像头 *1
====================================================
软件准备:
- Ubuntu系统镜像
- Mjpg-streamer
- gcc工具链
具体模型如下:
这里主要采用服务器来存储dragonboard通过摄像头采集到的数据,然后利用PC终端或是一顿段进行查看,在真正要实现外网访问必须要有公网IP或是采用内网映射的方法,而本人所在的网络为校园网,所以这里采用局域网的形式进行实现。具体实现如下:
1.首先安装libjpeg8-dev libv4l-dev subversion相关包:
- apt-get install libjpeg8-dev libv4l-dev subversion
2.从linux-sunxi.org中提供的仓库地址获取 mjpg-streamer源码
- svn co [url]https://svn.code.sf.net/p/mjpg-streamer/code/mjpg-streamer[/url] mjpg-streamer
3.进入mjpg-streamer并编译
- cd mjpg-streamer
- make USE_LIBV4L2=true clean all
4.运行start.sh启动脚本
框图如下:
到此可以基本实现局域网内监控了,不过为了更清楚项目源码实现过程,我们可以简单看下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}
- };
- 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);
- break;
- /* o, output */
- case 4:
- case 5:
- output[global.outcnt++] = strdup(optarg);
- break;
- /* v, version */
- case 6:
- case 7:
- printf("MJPG Streamer Version: %sn"
- "Compilation Date.....: %sn"
- "Compilation Time.....: %sn",
- }
-
- for(i = 0; i < global.outcnt; i++) {
- tmp = (size_t)(strchr(output[i], ' ') - output[i]);
- global.out[i].plugin = (tmp > 0) ? strndup(output[i], tmp) : strdup(output[i]);
- global.out[i].handle = dlopen(global.out[i].plugin, RTLD_LAZY);
- if(!global.out[i].handle) {
- LOG("ERROR: could not find output plugin %sn", global.out[i].plugin);
- LOG(" Perhaps you want to adjust the search path with:n");
- LOG(" # export LD_LIBRARY_PATH=/path/to/plugin/foldern");
- LOG(" dlopen: %sn", dlerror());
- closelog();
- exit(EXIT_FAILURE);
- }
- global.out[i].init = dlsym(global.out[i].handle, "output_init");
- if(global.out[i].init == NULL) {
- LOG("%sn", dlerror());
- exit(EXIT_FAILURE);
- }
- global.out[i].stop = dlsym(global.out[i].handle, "output_stop");
- if(global.out[i].stop == NULL) {
- LOG("%sn", dlerror());
- exit(EXIT_FAILURE);
- }
- global.out[i].run = dlsym(global.out[i].handle, "output_run");
- if(global.out[i].run == NULL) {
- LOG("%sn", dlerror());
- exit(EXIT_FAILURE);
- }
- /* try to find optional command */
- global.out[i].cmd = dlsym(global.out[i].handle, "output_cmd");
- global.out[i].param.parameters = strchr(output[i], ' ');
- split_parameters(global.out[i].param.parameters, &global.out[i].param.argc, global.out[i].param.argv);
- global.out[i].param.global = &global;
- global.out[i].param.id = i;
- if(global.out[i].init(&global.out[i].param, i)) {
- LOG("output_init() return value signals to exitn");
- closelog();
- exit(0);
- }
- }
- /* start to read the input, push pictures into global buffer */
- DBG("starting %d input pluginn", global.incnt);
- for(i = 0; i < global.incnt; i++) {
- syslog(LOG_INFO, "starting input plugin %s", global.in[i].plugin);
- if(global.in[i].run(i)) {
- LOG("can not run input plugin %d: %sn", i, global.in[i].plugin);
- closelog();
- return 1;
- }
- }
- 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);
- }
- /* wait for signals */
- pause();
- return 0;
- }
上面是整个项目的部分源码,整个程序的大体框图如下:
源码中getopt_long_only()函数主要是用来将*input中的-h -i -o -v -b的参数解析出来;而option_index是保存long_options中的坐标值,通过strdup(optarg)获取相应的标记后的参数;dlopen()函数负责打开*.so插件,dlsym()负责调用插件中的相关函数。具体还有相关的一些调用函数,感兴趣的话可以继续研究研究。
整个过程中涉及到v4l2接口、socket编程以及多线程编程。通过USB摄像头实现数据的采集(主要用到了Input_uvc.so模块),并将采集到的图像进行压缩处理,最红形成jpg格式图片上次到web上,客户端可以通过HTTP进行访问,具体效果如下图所示:
小结:
MJPG-streamer是用于从USB摄像头采集图像,把他们以流的形式通过基于ip的网络传输到浏览器从而降低服务器CPU的开销。因此对于嵌入式设备非常合适,同时由于无需为视频帧压缩,而大大提高CPU效率,这里只是做的简单研究和实现,上面情况是采用局域网来实现监控,如果要实现远程外网则需要一个公网IP,或者是利用花生壳做映射。后面附上MJPG-streamer源码。
关于基本环境搭建部分,和dragonboard底层驱动可以参考前面LED点灯实验,可以参考下面帖子。通过这些实验可以了解这个硬件GPIO的控制和使用,对dragonboard有个整体的把握和入门学习。
另外关于前面openCV主要实现了图像的特征检测和轮廓提取,在图像处理中主要设计有滤波和增强以及检测,具体可以参考下面帖子帖子。暂时分享到这里,后面有其他项目会继续和大家分享,谢谢!
附件: