友善之臂视频监控方案源码学习(1) - 架构分析

来源:互联网 发布:网络思想政治教育好处 编辑:程序博客网 时间:2024/06/06 04:14

转载于http://blog.csdn.net/tandesir/article/details/8373013

【说明】

对友善之臂的视频监控源码进行学习总结。如有错误,敬请指正。

 

【学习准备】

1 源码:http://download.csdn.net/detail/tandesir/4915905

2 工具:source insight 3

3 Fedora 10 + gcc4.5.1 + gdb

 

【源码树】

[tandesir@localhost ~]$ tree mjpg-streamer-mini2440-read-only/

[html] view plaincopy
  1. mjpg-streamer-mini2440-read-only/  
  2. |-- CHANGELOG  
  3. |-- LICENSE  
  4. |-- Makefile  
  5. |-- README  
  6. |-- mjpg-streamer-mini2440-bin.tar.gz  
  7. |-- mjpg-streamer-mini2440.kdev4  
  8. |-- mjpg_streamer.c  
  9. |-- mjpg_streamer.h  
  10. |-- plugins  
  11. |   |-- input.h  
  12. |   |-- input_control  
  13. |   |   |-- Makefile  
  14. |   |   |-- dynctrl.c  
  15. |   |   |-- dynctrl.h  
  16. |   |   |-- dynctrl.lo  
  17. |   |   |-- input_control.so  
  18. |   |   |-- input_uvc.c  
  19. |   |   |-- uvc_compat.h  
  20. |   |   `-- uvcvideo.h  
  21. |   |-- input_file  
  22. |   |   |-- Makefile  
  23. |   |   |-- input_file.c  
  24. |   |   `-- input_file.so  
  25. |   |-- input_gspcav1  
  26. |   |   |-- Makefile  
  27. |   |   |-- encoder.c  
  28. |   |   |-- encoder.h  
  29. |   |   |-- huffman.c  
  30. |   |   |-- huffman.h  
  31. |   |   |-- input_gspcav1.c  
  32. |   |   |-- jconfig.h  
  33. |   |   |-- jdatatype.h  
  34. |   |   |-- marker.c  
  35. |   |   |-- marker.h  
  36. |   |   |-- quant.c  
  37. |   |   |-- quant.h  
  38. |   |   |-- readme.spcacat  
  39. |   |   |-- spcaframe.h  
  40. |   |   |-- spcav4l.c  
  41. |   |   |-- spcav4l.h  
  42. |   |   |-- utils.c  
  43. |   |   `-- utils.h  
  44. |   |-- input_s3c2410  
  45. |   |   |-- Makefile  
  46. |   |   |-- input_s3c2410.c  
  47. |   |   |-- readme.s3c2410  
  48. |   |   |-- s3c2410.c  
  49. |   |   |-- s3c2410.h  
  50. |   |   |-- utils.c  
  51. |   |   `-- utils.h  
  52. |   |-- input_testpicture  
  53. |   |   |-- Makefile  
  54. |   |   |-- input_testpicture.c  
  55. |   |   |-- pictures  
  56. |   |   |   |-- 160x120_1.jpg  
  57. |   |   |   |-- 160x120_2.jpg  
  58. |   |   |   |-- 320x240_1.jpg  
  59. |   |   |   |-- 320x240_2.jpg  
  60. |   |   |   |-- 640x480_1.jpg  
  61. |   |   |   |-- 640x480_2.jpg  
  62. |   |   |   |-- 960x720_1.jpg  
  63. |   |   |   `-- 960x720_2.jpg  
  64. |   |   `-- testpictures.h  
  65. |   |-- input_uvc  
  66. |   |   |-- Makefile  
  67. |   |   |-- dynctrl.c  
  68. |   |   |-- dynctrl.h  
  69. |   |   |-- huffman.h  
  70. |   |   |-- input_uvc.c  
  71. |   |   |-- jpeg_utils.c  
  72. |   |   |-- jpeg_utils.h  
  73. |   |   |-- uvc_compat.h  
  74. |   |   |-- uvcvideo.h  
  75. |   |   |-- v4l2uvc.c  
  76. |   |   `-- v4l2uvc.h  
  77. |   |-- output.h  
  78. |   |-- output_autofocus  
  79. |   |   |-- Makefile  
  80. |   |   |-- output_autofocus.c  
  81. |   |   |-- processJPEG_onlyCenter.c  
  82. |   |   `-- processJPEG_onlyCenter.h  
  83. |   |-- output_file  
  84. |   |   |-- Makefile  
  85. |   |   `-- output_file.c  
  86. |   `-- output_http  
  87. |       |-- Makefile  
  88. |       |-- httpd.c  
  89. |       |-- httpd.h  
  90. |       `-- output_http.c  
  91. |-- simplified_jpeg_encoder.c  
  92. |-- simplified_jpeg_encoder.h  
  93. |-- start_s3c2410.sh  
  94. |-- start_uvc.sh  
  95. |-- start_uvc_yuv.sh  
  96. |-- utils.c  
  97. |-- utils.h  
  98. `-- www  
  99.     |-- LICENSE.txt  
  100.     |-- bodybg.gif  
  101.     |-- cambozola.jar  
  102.     |-- control.htm  
  103.     |-- example.jpg  
  104.     |-- favicon.ico  
  105.     |-- favicon.png  
  106.     |-- fix.css  
  107.     |-- functions.js  
  108.     |-- index.html  
  109.     |-- java.html  
  110.     |-- java_control.html  
  111.     |-- java_simple.html  
  112.     |-- javascript.html  
  113.     |-- javascript_motiondetection.html  
  114.     |-- javascript_simple.html  
  115.     |-- sidebarbg.gif  
  116.     |-- static.html  
  117.     |-- static_simple.html  
  118.     |-- stream.html  
  119.     |-- stream_simple.html  
  120.     |-- style.css  
  121.     `-- videolan.html  


【源码架构】

1 Makefile分析

顶层的Makefile如下所示:

[html] view plaincopy
  1. ###############################################################  
  2. #  
  3. # Purpose: Makefile for "M-JPEG Streamer"  
  4. # Author.: Tom Stoeveken (TST)  
  5. # Version: 0.3  
  6. # License: GPL  
  7. #  
  8. ###############################################################  
  9.   
  10. #CC = arm-linux-gcc  
  11. CC ?= gcc  
  12.   
  13. CFLAGS += -O3 -DLINUX -D_GNU_SOURCE -Wall   
  14. #CFLAGS += -O2 -DDEBUG -DLINUX -D_GNU_SOURCE -Wall  
  15. LFLAGS +=  -lpthread -ldl   
  16.   
  17. APP_BINARY=mjpg_streamer  
  18. OBJECTS=mjpg_streamer.o utils.o  
  19.   
  20. # define the names and targets of the plugins  
  21. PLUGINS = input_uvc.so  
  22. PLUGINS += output_file.so  
  23. PLUGINS += output_http.so  
  24. PLUGINS += input_testpicture.so  
  25. PLUGINS += output_autofocus.so  
  26. PLUGINS += input_gspcav1.so  
  27. PLUGINS += input_file.so  
  28. PLUGINS += input_control.so  
  29. # PLUGINS += input_http.so  
  30. # PLUGINS += output_viewer.so  
  31.   
  32.   
  33. all: application plugins  
  34.   
  35. clean:  
  36.     make -C plugins/input_uvc $@  
  37.     make -C plugins/input_testpicture $@  
  38.     make -C plugins/output_file $@  
  39.     make -C plugins/output_http $@  
  40.     make -C plugins/output_autofocus $@  
  41.     make -C plugins/input_gspcav1 $@  
  42.     rm -f *.a *.o $(APP_BINARY) core *~ *.so *.lo test_jpeg  
  43.   
  44. plugins: $(PLUGINS)  
  45.   
  46. #input_testpicture.so output_autofocus.so input_gspcav1.so  
  47.   
  48. application: $(APP_BINARY)  
  49.   
  50. output_autofocus.so: mjpg_streamer.h utils.h  
  51.     make -C plugins/output_autofocus all CC=$(CC)  
  52.     cp plugins/output_autofocus/output_autofocus.so .  
  53.   
  54. input_testpicture.so: mjpg_streamer.h utils.h  
  55.     make -C plugins/input_testpicture all CC=$(CC)  
  56.     cp plugins/input_testpicture/input_testpicture.so .  
  57.   
  58. input_uvc.so: mjpg_streamer.h utils.h  
  59.     make -C plugins/input_uvc all CC=$(CC)  
  60.     cp plugins/input_uvc/input_uvc.so .  
  61.   
  62. output_file.so: mjpg_streamer.h utils.h  
  63.     make -C plugins/output_file all CC=$(CC)  
  64.     cp plugins/output_file/output_file.so .  
  65.   
  66. output_http.so: mjpg_streamer.h utils.h  
  67.     make -C plugins/output_http all CC=$(CC)  
  68.     cp plugins/output_http/output_http.so .  
  69.   
  70. input_gspcav1.so: mjpg_streamer.h utils.h  
  71.     make -C plugins/input_gspcav1 all CC=$(CC)  
  72.     cp plugins/input_gspcav1/input_gspcav1.so .  
  73.   
  74. input_file.so: mjpg_streamer.h utils.h  
  75.     make -C plugins/input_file all  
  76.     cp plugins/input_file/input_file.so .     
  77.   
  78. input_control.so: mjpg_streamer.h utils.h  
  79.     make -C plugins/input_control all  
  80.     cp plugins/input_control/input_control.so .  
  81.   
  82. $(APP_BINARY): mjpg_streamer.c mjpg_streamer.h mjpg_streamer.o utils.c utils.h utils.o  
  83.     $(CC) $(CFLAGS) $(LFLAGS) $(OBJECTS) -o $(APP_BINARY)  
  84.     chmod 755 $(APP_BINARY)  
  85.   
  86. package: application plugins  
  87.     tar czvf mjpg-streamer-mini2440-bin.tar.gz \  
  88.     --exclude www/.svn \  
  89.   mjpg_streamer \  
  90.     input_testpicture.so \  
  91.     input_uvc.so \  
  92.   output_file.so \  
  93.     output_http.so \  
  94.     start_uvc.sh \  
  95.     start_uvc_yuv.sh \  
  96.   www \  
  97.   LICENSE  
  98.     
  99. test_jpeg: test_jpeg.c simplified_jpeg_encoder.c simplified_jpeg_encoder.h  
  100.     gcc -O0 -g simplified_jpeg_encoder.c test_jpeg.c  -o test_jpeg  
  101.     


由Makefile可以看出,经过编译将编译出如下文件:

(1) input plugins(临时文件) : input_uvc.so, input_testpicture.so, input_gspcav1.so, input_file.so, input_control.so, input_http.so

(2) output plugins(临时文件) : output_file.so, output_http.so, output_autofocus.so, output_viewer.so

(3)  目标(临时文件) : mjpg_streamer.o utils.o

(4)  可执行文件 : mjpg_streamer

 

2 main入口函数

main入口函数在mjpg_streamer.c,其执行过程可概括如下:

(1) 参数解析

1) 参数解析利用了option结构,其定义在getopt.h中:

[html] view plaincopy
  1. struct option  
  2. {  
  3. #if defined (__STDC__) && __STDC__  
  4.   const char *name;  
  5. #else  
  6.   char *name;  
  7. #endif  
  8.   /* has_arg can't be an enum because some compilers complain about  
  9.      type mismatches in all the code that assumes it is an int.  */  
  10.   int has_arg;  
  11.   int *flag;  
  12.   int val;  
  13. };  

(a) 基本概念

[html] view plaincopy
  1. myprog -a vv --add -b --file a.txt b.txt -e c.txt  

长选项(带--的选项):--add --file

短选项(带-的选项):-a -b -e

(b) 参数解释

name:不带短横线的选项名,前面没有短横线。譬如“help”、“verbose”之类。

ihas_arg: 描述了选项是否有选项参数。如果有,是哪种类型的参数。此时,它的值一定是下表中的一个。符号常量数值含义

no_argument  0 选项没有参数

required_argument  1 选项需要参数

optional_argument  2 选项参数可选

flag:指明长选项如何返回,如果flag为NULL,则getopt_long返回val。否则返回0,flag指向一个值为val的变量。如果该长选项没有发现,flag保持不变;

val:指明返回的值,或者需要加载到被flag所指示的变量中。

2)

[html] view plaincopy
  1. int option_index = 0c=0;  
  2.    static struct option long_options[] = \  
  3.    {  
  4.      {"h", no_argument, 0, 0},  
  5.      {"help", no_argument, 0, 0},  
  6.      {"i", required_argument, 0, 0},  
  7.      {"input", required_argument, 0, 0},  
  8.      {"o", required_argument, 0, 0},  
  9.      {"output", required_argument, 0, 0},  
  10.      {"v", no_argument, 0, 0},  
  11.      {"version", no_argument, 0, 0},  
  12.      {"b", no_argument, 0, 0},  
  13.      {"background", no_argument, 0, 0},  
  14.      {0, 0, 0, 0}  
  15.    };  

struct option最后一项须全部初始化为0。可见,input和output选项需要输入参数,其余选项无需参数。上述初始化,还表明,如果参数解析成功将返回val, 即0。

 

3) 参数解析采用了getopt_long_only函数:

[html] view plaincopy
  1. c = getopt_long_only(argc, argv, "", long_options, &option_index);  

函数getopt_long()的工作方式类似于getopt(),不过它还能接收长选项。在接收长选项之前,我们必须定义个一个结构体数组变量longopts,指明我们希望获取的长选项。getopt_long_only类似于getopt_long,但是它把'-'开头的选项当作长选项来处理。如果该选项与长选项不匹配,而与短选项匹配,则可以作为短选项解析。错误情况的处理和getopt一样,只是返回'?'时,还可能是别的情况引起的:选项含糊不明确或者无关参数。

 

4) 参数解析过程如下所示:

[html] view plaincopy
  1. /* no more options to parse */  
  2.     if (c == -1) break;  
  3.   
  4.     /* unrecognized option */  
  5.     if(c=='?'){ help(argv[0]); return 0; }  
  6.   
  7.     switch (option_index) {  
  8.       /* h, help */  
  9.       case 0:  
  10.       case 1:  
  11.         help(argv[0]);  
  12.         return 0;  
  13.         break;  
  14.   
  15.       /* i, input */  
  16.       case 2:  
  17.       case 3:  
  18.         input = strdup(optarg);  
  19.         break;  
  20.   
  21.       /* o, output */  
  22.       case 4:  
  23.       case 5:  
  24.         output[global.outcnt++] = strdup(optarg);  
  25.         break;  
  26.   
  27.       /* v, version */  
  28.       case 6:  
  29.       case 7:  
  30.         printf("MJPG Streamer Version: %s\n" \  
  31.                "Compilation Date.....: %s\n" \  
  32.                "Compilation Time.....: %s\n", SOURCE_VERSION, __DATE__, __TIME__);  
  33.         return 0;  
  34.         break;  
  35.   
  36.       /* b, background */  
  37.       case 8:  
  38.       case 9:  
  39.         daemon=1;  
  40.         break;  
  41.   
  42.       default:  
  43.         help(argv[0]);  
  44.         return 0;  
  45.     }  

整个解析的代码在while(1)循环内进行,若返回值为-1,则跳出循环。

 

(2) 根据参数判定是否创建守护进程

[html] view plaincopy
  1. /* fork to the background */  
  2.   if ( daemon ) {  
  3.     LOG("enabling daemon mode");  
  4.     daemon_mode();  
  5.   }  


(3) 初始化互斥锁

[html] view plaincopy
  1. if( pthread_mutex_init(&global.db, NULL) != 0 ) {  
  2.     LOG("could not initialize mutex variable\n");  
  3.     closelog();  
  4.     exit(EXIT_FAILURE);  
  5.   }  
  6.   if( pthread_cond_init(&global.db_update, NULL) != 0 ) {  
  7.     LOG("could not initialize condition variable\n");  
  8.     closelog();  
  9.     exit(EXIT_FAILURE);  
  10.   }  


(4) 检测终止信号(Ctrl + C)

[html] view plaincopy
  1. /* ignore SIGPIPE (send by OS if transmitting to closed TCP sockets) */  
  2.   signal(SIGPIPE, SIG_IGN);  
  3.   
  4.   /* register signal handler for <CTRL>+C in order to clean up */  
  5.   if (signal(SIGINT, signal_handler) == SIG_ERR) {  
  6.     LOG("could not register signal handler\n");  
  7.     closelog();  
  8.     exit(EXIT_FAILURE);  
  9.   }  

回调函数定义如下:

[html] view plaincopy
  1. void signal_handler(int sig)  
  2. {  
  3.   int i;  
  4.   
  5.   /* signal "stop" to threads */  
  6.   LOG("setting signal to stop\n");  
  7.   global.stop = 1;  
  8.   usleep(1000*1000);  
  9.   
  10.   /* clean up threads */  
  11.   LOG("force cancelation of threads and cleanup ressources\n");  
  12.   global.in.stop();  
  13.   for(i=0; i<global.outcnt; i++) {  
  14.     global.out[i].stop(global.out[i].param.id);  
  15.   }  
  16.   usleep(1000*1000);  
  17.   
  18.   /* close handles of input plugins */  
  19.   dlclose(&global.in.handle);  
  20.   for(i=0; i<global.outcnt; i++) {  
  21.     /* skip = 0;  
  22.     DBG("about to decrement usage counter for handle of %s, id #%02d, handle: %p\n", \  
  23.         global.out[i].plugin, global.out[i].param.id, global.out[i].handle);  
  24.     for(j=i+1; j<global.outcnt; j++) {  
  25.       if ( global.out[i].handle == global.out[j].handle ) {  
  26.         DBG("handles are pointing to the same destination (%p == %p)\n", global.out[i].handle, global.out[j].handle);  
  27.         skip = 1;  
  28.       }  
  29.     }  
  30.     if ( skip ) {  
  31.       continue;  
  32.     }  
  33.   
  34.     DBG("closing handle %p\n", global.out[i].handle);  
  35.     */  
  36.     dlclose(global.out[i].handle);  
  37.   }  
  38.   DBG("all plugin handles closed\n");  
  39.   
  40.   pthread_cond_destroy(&global.db_update);  
  41.   pthread_mutex_destroy(&global.db);  
  42.   
  43.   LOG("done\n");  
  44.   
  45.   closelog();  
  46.   exit(0);  
  47.   return;  
  48. }  

主要作用是释放资源。


(5) 加载相应的输入、输出动态链接库

[html] view plaincopy
  1. /* open input plugin */  
  2. tmp = (size_t)(strchr(input, ' ')-input);  
  3. global.in.plugin = (tmp > 0)?strndup(input, tmp):strdup(input);  
  4. global.in.handle = dlopen(global.in.plugin, RTLD_LAZY);  
  5. if ( !global.in.handle ) {  
  6.   LOG("ERROR: could not find input plugin\n");  
  7.   LOG("       Perhaps you want to adjust the search path with:\n");  
  8.   LOG("       # export LD_LIBRARY_PATH=/path/to/plugin/folder\n");  
  9.   LOG("       dlopen: %s\n", dlerror() );  
  10.   closelog();  
  11.   exit(EXIT_FAILURE);  
  12. }  
  13. global.in.init = dlsym(global.in.handle, "input_init");  
  14. if ( global.in.init == NULL ) {  
  15.   LOG("%s\n", dlerror());  
  16.   exit(EXIT_FAILURE);  
  17. }  
  18. global.in.stop = dlsym(global.in.handle, "input_stop");  
  19. if ( global.in.stop == NULL ) {  
  20.   LOG("%s\n", dlerror());  
  21.   exit(EXIT_FAILURE);  
  22. }  
  23. global.in.run = dlsym(global.in.handle, "input_run");  
  24. if ( global.in.run == NULL ) {  
  25.   LOG("%s\n", dlerror());  
  26.   exit(EXIT_FAILURE);  
  27. }  

上述代码为输入插件的情况,输出与之相似。global的结构如下:

[html] view plaincopy
  1. struct _globals {  
  2.   int stop;  
  3.   
  4.   /* signal fresh frames */  
  5.   pthread_mutex_t db;  
  6.   pthread_cond_t  db_update;  
  7.   
  8.   /* global JPG frame, this is more or less the "database" */  
  9.   unsigned char *buf;  
  10.   int size;  
  11.   
  12.   /* input plugin */  
  13.   input in;  
  14.   
  15.   /* output plugin */  
  16.   output out[MAX_OUTPUT_PLUGINS];  
  17.   int outcnt;  
  18.   
  19.   /* pointer to control functions */  
  20.   int (*control)(int command, char *details);  
  21. };  

该结构中,定义了互斥锁,以及输入、输出、控制结构。output和input类似。input的结构如下:

[html] view plaincopy
  1. struct _input {  
  2.   char *plugin;  
  3.   void *handle;  
  4.   input_parameter param;  
  5.   
  6.   int (*init)(input_parameter *);  
  7.   int (*stop)(void);  
  8.   int (*run)(void);  
  9.   int (*cmd)(in_cmd_type, int);  
  10. };  

由此可见,加载动态链接库的过程实质上,是对input、output结构的函数指针进行初试化。输入的input定义如下:

[html] view plaincopy
  1. char *input  = "input_uvc.so --resolution 640x480 --fps 5 --device /dev/video0";  

下述代码实质上,是取出了input_uvc.so

[html] view plaincopy
  1. tmp = (size_t)(strchr(input, ' ')-input);  
  2. global.in.plugin = (tmp > 0)?strndup(input, tmp):strdup(input);  

动态链接库的加载利用了dlopen函数:

[html] view plaincopy
  1. global.in.handle = dlopen(global.in.plugin, RTLD_LAZY);  

调用动态链接库使用了dlsym函数:

[html] view plaincopy
  1. global.in.init = dlsym(global.in.handle, "input_init");  

"input_init"为函数接口的名称,声明在plugins目录下的input.h文件中,具体实现在plugins子目录中。

 

(6) 运行程序

[html] view plaincopy
  1. /* start to read the input, push pictures into global buffer */  
  2.   DBG("starting input plugin\n");  
  3.   syslog(LOG_INFO, "starting input plugin");  
  4.   if ( global.in.run() ) {  
  5.     LOG("can not run input plugin\n");  
  6.     closelog();  
  7.     return 1;  
  8.   }  

上述代码为输入插件的情况,输出与之相似。运行加载的run函数。

 

(7) 其他初始化

 

(8) 参数配置

参数配置在类似start_uvc_yuv.sh的shell脚本中,其形式如下:

[html] view plaincopy
  1. ./mjpg_streamer -o "output_http.so -w ./www" -i "input_uvc.so -y -d /dev/video2"  

 

【代码评述】

总体来看,代码利用动态链接库实现了功能的动态加载,其加载过程在运行时发生。运行start_uvc_yuv.sh之类的shell脚本时,mjpg_streamer首先解析参数,然后根据参数加载对应的动态链接库,实现相应的功能。

 

 

转载请标明出处,仅供学习交流,勿用于商业目的

Copyright @ http://blog.csdn.net/tandesir

原创粉丝点击