mjpg-streamer項目源碼分析

  轉載來自:http://blog.csdn.net/pengrui18/article/details/8146814

前一段時間自己買了個開發板(GT2440的),可是我沒有夠相應的買cmos攝像頭,可是又想做下國嵌的usb視頻採集和傳輸的哪個項目,沒辦法,只好網上找找相關的項目,最終發現了mjpg-streamer這個開源項目。看了blog們的文章,有種激動,於是自己問同學借了個usb攝像頭,試了試,挺好使的,而且處理速度上也挺好的,就開始想了解這個項目是怎麼工作了(研究了好幾天哦)。

下面是我個人在這學習mjpg-streamer時的一些看法,不足之處,還請大家多多指出:

首先,mjpg-streamer目錄由以下目錄組成:

.deps 

doc 

mjpeg-client //這個目錄包含我們使用的客服端

mjpg-streamer //這個是usb攝像頭採集和傳輸的目錄

mjpg-streamer-experiment

udp-client

uvc-streamer.

這裏我們只分析下mjpg-streamer目錄,下面看下上面的mjpg-streamer目錄的組成:


plugins目錄:主要是一些usb攝像頭的數據採集和傳輸的功能子函數

scripts目錄貌似沒啥用

www目錄主要是在使用瀏覽器瀏覽時,可以增加html界面上一些功能。

mjpg-streamer.c文件夾,主要實現命令參數的解析及調用相關線程運行功能子函數

下面我們看下mjpg-streamer.c中的源代碼:

  1. int main(int argc, char *argv[])  
  2. {  
  3.   char *input  = "input_uvc.so --resolution 640x480 --fps 5 --device /dev/video0";  
  4.   char *output[MAX_OUTPUT_PLUGINS];//10  
  5.   int daemon=0, i;  
  6.   size_t tmp=0;  
  7.   
  8.   output[0] = "output_http.so --port 8080";  
  9.   global.outcnt = 0;  
  10.   
  11.   global.control = control;  
  12.   
  13.   /* parameter parsing */  
  14.   while(1) {  
  15.     int option_index = 0c=0;  
  16.     static struct option long_options[] = \  
  17.     {  
  18.       {"h", no_argument, 0, 0},  
  19.       {"help", no_argument, 0, 0},  
  20.       {"i", required_argument, 0, 0},  
  21.       {"input", required_argument, 0, 0},  
  22.       {"o", required_argument, 0, 0},  
  23.       {"output", required_argument, 0, 0},  
  24.       {"v", no_argument, 0, 0},  
  25.       {"version", no_argument, 0, 0},  
  26.       {"b", no_argument, 0, 0},  
  27.       {"background", no_argument, 0, 0},  
  28.       {0, 0, 0, 0}  
  29.     };  
  30.   
  31.     c = getopt_long_only(argc, argv, "", long_options, &option_index);  

這裏主要遇到一個功能函數getopt_long_only(),該函數功能主要是解析命令行選項,也就是將*input中的-h -i -o -v -b的參數解析出來,該函數具體如何使用,可參照getopt_long_only.

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

option_index保存的是上面的long_options中的座標值,strdup(optarg)用來獲取相應的標記後的參數,例如當main函數中傳入的命令爲./mjpg_streamer -i "./input_uvc.so"時,

程序將執行到case3:中,然後將input="./input_uvc.so".

至此,前面就幫我們將輸入的mjpg-streamer命令讀解析好了。

下面開始調用相應的函數:

  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.   }  

dlopen()函數負責打開*.so插件,dlsym()負責調用插件中的相關函數。目前意思已經很明顯了,當輸入./mjpg_streamer -i "./input_uvc.so"時,系統將會調用input_uvc.so中的input_init函數,對輸入設備進行初始化。我們打開plugins\input_uvc\input_uvc.c看下是否有input_init()函數,確實存在,這說明我前面的理解沒錯。

既然輸入已經初始化了,那輸出呢?不急,我們繼續看下下面的代碼:

  1. /* open output plugin */  
  2.   for (i=0; i<global.outcnt; i++) {  
  3.     tmp = (size_t)(strchr(output[i], ' ')-output[i]);  
  4.     global.out[i].plugin = (tmp > 0)?strndup(output[i], tmp):strdup(output[i]);  
  5.     global.out[i].handle = dlopen(global.out[i].plugin, RTLD_LAZY);  
  6.     if ( !global.out[i].handle ) {  
  7.       LOG("ERROR: could not find output plugin %s\n", global.out[i].plugin);  
  8.       LOG("       Perhaps you want to adjust the search path with:\n");  
  9.       LOG("       # export LD_LIBRARY_PATH=/path/to/plugin/folder\n");  
  10.       LOG("       dlopen: %s\n", dlerror() );  
  11.       closelog();  
  12.       exit(EXIT_FAILURE);  
  13.     }  
  14.     global.out[i].init = dlsym(global.out[i].handle, "output_init");  
  15.     if ( global.out[i].init == NULL ) {  
  16.       LOG("%s\n", dlerror());  
  17.       exit(EXIT_FAILURE);  
  18.     }  
  19.     global.out[i].stop = dlsym(global.out[i].handle, "output_stop");  
  20.     if ( global.out[i].stop == NULL ) {  
  21.       LOG("%s\n", dlerror());  
  22.       exit(EXIT_FAILURE);  
  23.     }  
  24.     if ( global.out[i].stop == NULL ) {  
  25.       LOG("%s\n", dlerror());  
  26.       exit(EXIT_FAILURE);  
  27.     }  
  28.     global.out[i].run = dlsym(global.out[i].handle, "output_run");  
  29.     if ( global.out[i].run == NULL ) {  
  30.       LOG("%s\n", dlerror());  
  31.       exit(EXIT_FAILURE);  
  32.     }  
這段代碼就不分析了,相信大家已經很明白了。

嗯,輸入和輸出設備都準備,下面該幹啥了,不用說肯定幹活了嗎。俗話說“養軍千日用在一時”:

  1. if ( global.in.run() ) {  
  2.     LOG("can not run input plugin\n");  
  3.     closelog();  
  4.     return 1;  
  5.   }  
  6.   
  7. for(i=0; i<global.outcnt; i++) {  
  8.     syslog(LOG_INFO, "starting output plugin: %s (ID: %02d)", global.out[i].plugin, global.out[i].param.id);  
  9.     global.out[i].run(global.out[i].param.id);  
  10. }  
mjpg-streamer就這樣結束了,貌似還真不過癮,下面我們到input_uvc.c和output_http.c文件看下:

input_uvc.c(plugins\input_uvc\)

  1. int input_init(input_parameter *param)   
這個函數貌似挺長的,其實代碼也就只是實現對usb攝像頭的格式、幀、請求buf,隊列buf等的一些設置,主要實現也是在函數init_videoIn(videoIn, dev, width, height, fps, format, 1) 中,有興趣的話,大家可以去看看。

  1. int input_stop(void) {  
  2.   DBG("will cancel input thread\n");  
  3.   pthread_cancel(cam);  
  4.   
  5.   return 0;  
  6. }  

輸入停止,貌似更加簡單,只要將線程取下cancel就ok,那麼run肯定是crete threads了。

  1. int input_run(void) {  
  2.   pglobal->buf = malloc(videoIn->framesizeIn);  
  3.   if (pglobal->buf == NULL) {  
  4.     fprintf(stderr, "could not allocate memory\n");  
  5.     exit(EXIT_FAILURE);  
  6.   }  
  7.   
  8.   pthread_create(&cam, 0, cam_thread, NULL);  
  9.   pthread_detach(cam);  
  10.   
  11.   return 0;  
  12. }  

猜對了,可惜沒分加。下面那些代碼基本也沒用到,這就不細說了。

output_http.c(plugins\output_http\)

剛開始也肯定是對其初始化,然後stop,最後run。其實我挺喜歡這個項目的編寫者,處處爲咱們考慮。

  1. int output_init(output_parameter *param) {  
  2.   servers[param->id].id = param->id;  
  3.   servers[param->id].pglobal = param->global;  
  4.   servers[param->id].conf.port = port;  
  5.   servers[param->id].conf.credentials = credentials;  
  6.   servers[param->id].conf.www_folder = www_folder;  
  7.   servers[param->id].conf.nocommands = nocommands;  
  8. }  
  9. int output_stop(int id) {  
  10.   
  11.   DBG("will cancel server thread #%02d\n", id);  
  12.   pthread_cancel(servers[id].threadID);  
  13.   
  14.   return 0;  
  15. }  
  16.   
  17. int output_run(int id) {  
  18.   DBG("launching server thread #%02d\n", id);  
  19.   
  20.   /* create thread and pass context to thread function */  
  21.   pthread_create(&(servers[id].threadID), NULL, server_thread, &(servers[id]));  
  22.   pthread_detach(servers[id].threadID);  
  23.   
  24.   return 0;  
原理圖如下所示:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章