无线视频监控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;}
原创粉丝点击