/******************************************************************************* # # # 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for videodev2.h */ #include #include "utils.h" #include "mjpg_streamer.h" /* globals */ static globals global; /****************************************************************************** Description.: Display a help message Input Value.: argv[0] is the program name and the parameter progname Return Value: - ******************************************************************************/ static void help(char *progname) { fprintf(stderr, "-----------------------------------------------------------------------\n"); fprintf(stderr, "Usage: %s\n" \ " -i | --input \" [parameters]\"\n" \ " -o | --output \" [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 resources. The function prototype is defined by the system, because it is a callback function. Input Value.: sig tells us which signal was received Return Value: - ******************************************************************************/ static 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 cancellation of threads and cleanup resources\n"); for(i = 0; i < global.incnt; i++) { global.in[i].stop(i); /*for (j = 0; j= MAX_PLUGIN_ARGUMENTS) { IPRINT("ERROR: too many arguments to input plugin\n"); return 0; } } } } free(arg); } *argc = count; return 1; } /****************************************************************************** Description.: Input Value.: Return Value: ******************************************************************************/ 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, j; size_t tmp = 0; output[0] = "output_http.so --port 8080"; global.outcnt = 0; global.incnt = 0; /* parameter parsing */ while(1) { int c = 0; static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"input", required_argument, NULL, 'i'}, {"output", required_argument, NULL, 'o'}, {"version", no_argument, NULL, 'v'}, {"background", no_argument, NULL, 'b'}, {NULL, 0, NULL, 0} }; c = getopt_long(argc, argv, "hi:o:vb", long_options, NULL); /* no more options to parse */ if(c == -1) break; switch(c) { case 'i': input[global.incnt++] = strdup(optarg); break; case 'o': output[global.outcnt++] = strdup(optarg); break; case 'v': printf("MJPG Streamer Version: %s\n", #ifdef GIT_HASH GIT_HASH #else SOURCE_VERSION #endif ); return 0; break; case 'b': daemon = 1; break; case 'h': /* fall through */ default: help(argv[0]); exit(EXIT_FAILURE); } } 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"); /* fork to the background */ if(daemon) { LOG("enabling daemon mode"); daemon_mode(); } /* ignore SIGPIPE (send by OS if transmitting to closed TCP sockets) */ signal(SIGPIPE, SIG_IGN); /* register signal handler for +C in order to clean up */ 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 */ #ifdef GIT_HASH LOG("MJPG Streamer Version: git rev: %s\n", GIT_HASH); #else LOG("MJPG Streamer Version.: %s\n", SOURCE_VERSION); #endif /* check if at least one output plugin was selected */ if(global.outcnt == 0) { /* no? Then use the default plugin instead */ global.outcnt = 1; } /* open input plugin */ for(i = 0; i < global.incnt; i++) { /* this mutex and the conditional variable are used to synchronize access to the global picture buffer */ if(pthread_mutex_init(&global.in[i].db, NULL) != 0) { LOG("could not initialize mutex variable\n"); closelog(); exit(EXIT_FAILURE); } if(pthread_cond_init(&global.in[i].db_update, NULL) != 0) { LOG("could not initialize condition variable\n"); closelog(); exit(EXIT_FAILURE); } tmp = (size_t)(strchr(input[i], ' ') - input[i]); global.in[i].stop = 0; global.in[i].context = NULL; global.in[i].buf = NULL; global.in[i].size = 0; global.in[i].plugin = (tmp > 0) ? strndup(input[i], tmp) : strdup(input[i]); global.in[i].handle = dlopen(global.in[i].plugin, RTLD_LAZY); if(!global.in[i].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[i].init = dlsym(global.in[i].handle, "input_init"); if(global.in[i].init == NULL) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); } global.in[i].stop = dlsym(global.in[i].handle, "input_stop"); if(global.in[i].stop == NULL) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); } global.in[i].run = dlsym(global.in[i].handle, "input_run"); if(global.in[i].run == NULL) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); } /* try to find optional command */ global.in[i].cmd = dlsym(global.in[i].handle, "input_cmd"); global.in[i].param.parameters = strchr(input[i], ' '); for (j = 0; j 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); } global.out[i].run = dlsym(global.out[i].handle, "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].param.parameters = strchr(output[i], ' '); for (j = 0; j