您的位置:首页 > 运维架构 > Linux

mjpg-streamer项目源码分析 2

2014-06-19 23:22 375 查看
mjpg-streamer项目源码分析  

2013-09-09 10:41:05|  分类: GT2440 |举报|字号 订阅

 mjpg-streamer项目源码分析

2012-11-06 10:04 397人阅读 评论(2) 收藏 举报

        前一段时间自己买了个开发板(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中的源代码:

int main(int argc, char *argv[])  

{  

  char *input  = "input_uvc.so --resolution 640x480 --fps 5 --device /dev/video0";  

  char *output[MAX_OUTPUT_PLUGINS];//10  

  int daemon=0, i;  

  size_t tmp=0;  

  

  output[0] = "output_http.so --port 8080";  

  global.outcnt = 0;  

  

  global.control = control;  

  

  /* 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);  

这里主要遇到一个功能函数getopt_long_only(),该函数功能主要是解析命令行选项,也就是将*input中的-h -i -o -v -b的参数解析出来,该函数具体如何使用,可参照getopt_long_only.

  switch (option_index) {  

    /* h, help */  

    case 0:  

    case 1:  

      help(argv[0]);  

      return 0;  

      break;  

  

    /* i, input */  

    case 2:  

    case 3:  

      input = 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: %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;  

  }  

}  

option_index保存的是上面的long_options中的坐标值,strdup(optarg)用来获取相应的标记后的参数,例如当main函数中传入的命令为./mjpg_streamer -i "./input_uvc.so"时,

程序将执行到case3:中,然后将input="./input_uvc.so".

至此,前面就帮我们将输入的mjpg-streamer命令读解析好了。

下面开始调用相应的函数:

  /* open input plugin */  

  tmp = (size_t)(strchr(input, ' ')-input);  

  global.in.plugin = (tmp > 0)?strndup(input, tmp):strdup(input);  

  global.in.handle = dlopen(global.in.plugin, RTLD_LAZY);  

  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");  

  if ( global.in.init == NULL ) {  

    LOG("%s\n", dlerror());  

    exit(EXIT_FAILURE);  

  }  

  global.in.stop = dlsym(global.in.handle, "input_stop");  

  if ( global.in.stop == NULL ) {  

    LOG("%s\n", dlerror());  

    exit(EXIT_FAILURE);  

  }  

  global.in.run = dlsym(global.in.handle, "input_run");  

  if ( global.in.run == NULL ) {  

    LOG("%s\n", dlerror());  

    exit(EXIT_FAILURE);  

  }  

dlopen()函数负责打开*.so插件,dlsym()负责调用插件中的相关函数。目前意思已经很明显了,当输入./mjpg_streamer -i "./input_uvc.so"时,系统将会调用input_uvc.so中的input_init函数,对输入设备进行初始化。我们打开plugins\input_uvc\input_uvc.c看下是否有input_init()函数,确实存在,这说明我前面的理解没错。

既然输入已经初始化了,那输出呢?不急,我们继续看下下面的代码:

/* open output plugin */  

  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 %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");  

    if ( global.out[i].init == NULL ) {  

      LOG("%s\n", dlerror());  

      exit(EXIT_FAILURE);  

    }  

    global.out[i].stop = dlsym(global.out[i].handle, "output_stop");  

    if ( global.out[i].stop == NULL ) {  

      LOG("%s\n", dlerror());  

      exit(EXIT_FAILURE);  

    }  

    if ( global.out[i].stop == NULL ) {  

      LOG("%s\n", dlerror());  

      exit(EXIT_FAILURE);  

    }  

    global.out[i].run = dlsym(global.out[i].handle, "output_run");  

    if ( global.out[i].run == NULL ) {  

      LOG("%s\n", dlerror());  

      exit(EXIT_FAILURE);  

    }  

这段代码就不分析了,相信大家已经很明白了。

嗯,输入和输出设备都准备,下面该干啥了,不用说肯定干活了吗。俗话说“养军千日用在一时”:

if ( global.in.run() ) {  

    LOG("can not run input plugin\n");  

    closelog();  

    return 1;  

  }  

  

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);  

}  

mjpg-streamer就这样结束了,貌似还真不过瘾,下面我们到input_uvc.c和output_http.c文件看下:

input_uvc.c(plugins\input_uvc\)

int input_init(input_parameter *param)   

这个函数貌似挺长的,其实代码也就只是实现对usb摄像头的格式、帧、请求buf,队列buf等的一些设置,主要实现也是在函数init_videoIn(videoIn, dev, width, height, fps, format, 1) 中,有兴趣的话,大家可以去看看。

int input_stop(void) {  

  DBG("will cancel input thread\n");  

  pthread_cancel(cam);  

  

  return 0;  

}  

输入停止,貌似更加简单,只要将线程取下cancel就ok,那么run肯定是crete threads了。

int input_run(void) {  

  pglobal->buf = malloc(videoIn->framesizeIn);  

  if (pglobal->buf == NULL) {  

    fprintf(stderr, "could not allocate memory\n");  

    exit(EXIT_FAILURE);  

  }  

  

  pthread_create(&cam, 0, cam_thread, NULL);  

  pthread_detach(cam);  

  

  return 0;  

}  

猜对了,可惜没分加。下面那些代码基本也没用到,这就不细说了。

output_http.c(plugins\output_http\)

刚开始也肯定是对其初始化,然后stop,最后run。其实我挺喜欢这个项目的编写者,处处为咱们考虑。

int output_init(output_parameter *param) {  

  servers[param->id].id = param->id;  

  servers[param->id].pglobal = param->global;  

  servers[param->id].conf.port = port;  

  servers[param->id].conf.credentials = credentials;  

  servers[param->id].conf.www_folder = www_folder;  

  servers[param->id].conf.nocommands = nocommands;  

}  

int output_stop(int id) {  

  

  DBG("will cancel server thread #%02d\n", id);  

  pthread_cancel(servers[id].threadID);  

  

  return 0;  

}  

  

int output_run(int id) {  

  DBG("launching server thread #%02d\n", id);  

  

  /* create thread and pass context to thread function */  

  pthread_create(&(servers[id].threadID), NULL, server_thread, &(servers[id]));  

  pthread_detach(servers[id].threadID);  

  

  return 0;  

}  

我想这个项目分析的差不多了,至于一些小的方面,我不依依细说了,最后送大家一句古语“ 师傅领进门,修行靠个人”,并赠上这个项目的框架图。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息