mjpg_streamer.c
Go to the documentation of this file.
00001 /*******************************************************************************
00002 #                                                                              #
00003 #      MJPG-streamer allows to stream JPG frames from an input-plugin          #
00004 #      to several output plugins                                               #
00005 #                                                                              #
00006 #      Copyright (C) 2007 Tom Stöveken                                         #
00007 #                                                                              #
00008 # This program is free software; you can redistribute it and/or modify         #
00009 # it under the terms of the GNU General Public License as published by         #
00010 # the Free Software Foundation; version 2 of the License.                      #
00011 #                                                                              #
00012 # This program is distributed in the hope that it will be useful,              #
00013 # but WITHOUT ANY WARRANTY; without even the implied warranty of               #
00014 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                #
00015 # GNU General Public License for more details.                                 #
00016 #                                                                              #
00017 # You should have received a copy of the GNU General Public License            #
00018 # along with this program; if not, write to the Free Software                  #
00019 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA    #
00020 #                                                                              #
00021 *******************************************************************************/
00022 
00023 #include <stdio.h>
00024 #include <stdlib.h>
00025 #include <unistd.h>
00026 #include <string.h>
00027 #include <sys/ioctl.h>
00028 #include <errno.h>
00029 #include <signal.h>
00030 #include <sys/socket.h>
00031 #include <arpa/inet.h>
00032 #include <sys/types.h>
00033 #include <sys/stat.h>
00034 #include <getopt.h>
00035 #include <pthread.h>
00036 #include <dlfcn.h>
00037 #include <fcntl.h>
00038 #include <syslog.h>
00039 #include <linux/types.h>          /* for videodev2.h */
00040 #include <linux/videodev2.h>
00041 
00042 #include "utils.h"
00043 #include "mjpg_streamer.h"
00044 
00045 /* globals */
00046 static globals global;
00047 
00048 /******************************************************************************
00049 Description.: Display a help message
00050 Input Value.: argv[0] is the program name and the parameter progname
00051 Return Value: -
00052 ******************************************************************************/
00053 void help(char *progname)
00054 {
00055     fprintf(stderr, "-----------------------------------------------------------------------\n");
00056     fprintf(stderr, "Usage: %s\n" \
00057             "  -i | --input \"<input-plugin.so> [parameters]\"\n" \
00058             "  -o | --output \"<output-plugin.so> [parameters]\"\n" \
00059             " [-h | --help ]........: display this help\n" \
00060             " [-v | --version ].....: display version information\n" \
00061             " [-b | --background]...: fork to the background, daemon mode\n", progname);
00062     fprintf(stderr, "-----------------------------------------------------------------------\n");
00063     fprintf(stderr, "Example #1:\n" \
00064             " To open an UVC webcam \"/dev/video1\" and stream it via HTTP:\n" \
00065             "  %s -i \"input_uvc.so -d /dev/video1\" -o \"output_http.so\"\n", progname);
00066     fprintf(stderr, "-----------------------------------------------------------------------\n");
00067     fprintf(stderr, "Example #2:\n" \
00068             " To open an UVC webcam and stream via HTTP port 8090:\n" \
00069             "  %s -i \"input_uvc.so\" -o \"output_http.so -p 8090\"\n", progname);
00070     fprintf(stderr, "-----------------------------------------------------------------------\n");
00071     fprintf(stderr, "Example #3:\n" \
00072             " To get help for a certain input plugin:\n" \
00073             "  %s -i \"input_uvc.so --help\"\n", progname);
00074     fprintf(stderr, "-----------------------------------------------------------------------\n");
00075     fprintf(stderr, "In case the modules (=plugins) can not be found:\n" \
00076             " * Set the default search path for the modules with:\n" \
00077             "   export LD_LIBRARY_PATH=/path/to/plugins,\n" \
00078             " * or put the plugins into the \"/lib/\" or \"/usr/lib\" folder,\n" \
00079             " * or instead of just providing the plugin file name, use a complete\n" \
00080             "   path and filename:\n" \
00081             "   %s -i \"/path/to/modules/input_uvc.so\"\n", progname);
00082     fprintf(stderr, "-----------------------------------------------------------------------\n");
00083 }
00084 
00085 /******************************************************************************
00086 Description.: pressing CTRL+C sends signals to this process instead of just
00087               killing it plugins can tidily shutdown and free allocated
00088               resources. The function prototype is defined by the system,
00089               because it is a callback function.
00090 Input Value.: sig tells us which signal was received
00091 Return Value: -
00092 ******************************************************************************/
00093 void signal_handler(int sig)
00094 {
00095     int i, j;
00096 
00097     /* signal "stop" to threads */
00098     LOG("setting signal to stop\n");
00099     global.stop = 1;
00100     usleep(1000 * 1000);
00101 
00102     /* clean up threads */
00103     LOG("force cancellation of threads and cleanup resources\n");
00104     for(i = 0; i < global.incnt; i++) {
00105         global.in[i].stop(i);
00106         /*for (j = 0; j<MAX_PLUGIN_ARGUMENTS; j++) {
00107             if (global.in[i].param.argv[j] != NULL) {
00108                 free(global.in[i].param.argv[j]);
00109             }
00110         }*/
00111     }
00112 
00113     for(i = 0; i < global.outcnt; i++) {
00114         global.out[i].stop(global.out[i].param.id);
00115         pthread_cond_destroy(&global.in[i].db_update);
00116         pthread_mutex_destroy(&global.in[i].db);
00117         /*for (j = 0; j<MAX_PLUGIN_ARGUMENTS; j++) {
00118             if (global.out[i].param.argv[j] != NULL)
00119                 free(global.out[i].param.argv[j]);
00120         }*/
00121     }
00122     usleep(1000 * 1000);
00123 
00124     /* close handles of input plugins */
00125     for(i = 0; i < global.incnt; i++) {
00126         dlclose(global.in[i].handle);
00127     }
00128 
00129     for(i = 0; i < global.outcnt; i++) {
00130         int j, skip = 0;
00131         DBG("about to decrement usage counter for handle of %s, id #%02d, handle: %p\n", \
00132             global.out[i].plugin, global.out[i].param.id, global.out[i].handle);
00133 
00134         for(j=i+1; j<global.outcnt; j++) {
00135           if ( global.out[i].handle == global.out[j].handle ) {
00136             DBG("handles are pointing to the same destination (%p == %p)\n", global.out[i].handle, global.out[j].handle);
00137             skip = 1;
00138           }
00139         }
00140         if ( skip ) {
00141           continue;
00142         }
00143 
00144         DBG("closing handle %p\n", global.out[i].handle);
00145 
00146         dlclose(global.out[i].handle);
00147     }
00148     DBG("all plugin handles closed\n");
00149 
00150     LOG("done\n");
00151 
00152     closelog();
00153     exit(0);
00154     return;
00155 }
00156 
00157 int split_parameters(char *parameter_string, int *argc, char **argv)
00158 {
00159     int count = 1;
00160     argv[0] = NULL; // the plugin may set it to 'INPUT_PLUGIN_NAME'
00161     if(parameter_string != NULL && strlen(parameter_string) != 0) {
00162         char *arg = NULL, *saveptr = NULL, *token = NULL;
00163 
00164         arg = strdup(parameter_string);
00165 
00166         if(strchr(arg, ' ') != NULL) {
00167             token = strtok_r(arg, " ", &saveptr);
00168             if(token != NULL) {
00169                 argv[count] = strdup(token);
00170                 count++;
00171                 while((token = strtok_r(NULL, " ", &saveptr)) != NULL) {
00172                     argv[count] = strdup(token);
00173                     count++;
00174                     if(count >= MAX_PLUGIN_ARGUMENTS) {
00175                         IPRINT("ERROR: too many arguments to input plugin\n");
00176                         return 0;
00177                     }
00178                 }
00179             }
00180         }
00181         free(arg);
00182     }
00183     *argc = count;
00184     return 1;
00185 }
00186 
00187 /******************************************************************************
00188 Description.:
00189 Input Value.:
00190 Return Value:
00191 ******************************************************************************/
00192 int main(int argc, char *argv[])
00193 {
00194     //char *input  = "input_uvc.so --resolution 640x480 --fps 5 --device /dev/video0";
00195     char *input[MAX_INPUT_PLUGINS];
00196     char *output[MAX_OUTPUT_PLUGINS];
00197     int daemon = 0, i, j;
00198     size_t tmp = 0;
00199 
00200     output[0] = "output_http.so --port 8080";
00201     global.outcnt = 0;
00202     global.incnt = 0;
00203 
00204     /* parameter parsing */
00205     while(1) {
00206         int option_index = 0, c = 0;
00207         static struct option long_options[] = {
00208             {"h", no_argument, 0, 0
00209             },
00210             {"help", no_argument, 0, 0},
00211             {"i", required_argument, 0, 0},
00212             {"input", required_argument, 0, 0},
00213             {"o", required_argument, 0, 0},
00214             {"output", required_argument, 0, 0},
00215             {"v", no_argument, 0, 0},
00216             {"version", no_argument, 0, 0},
00217             {"b", no_argument, 0, 0},
00218             {"background", no_argument, 0, 0},
00219             {0, 0, 0, 0}
00220         };
00221 
00222         c = getopt_long_only(argc, argv, "", long_options, &option_index);
00223 
00224         /* no more options to parse */
00225         if(c == -1) break;
00226 
00227         /* unrecognized option */
00228         if(c == '?') {
00229             help(argv[0]);
00230             return 0;
00231         }
00232 
00233         switch(option_index) {
00234             /* h, help */
00235         case 0:
00236         case 1:
00237             help(argv[0]);
00238             return 0;
00239             break;
00240 
00241             /* i, input */
00242         case 2:
00243         case 3:
00244             input[global.incnt++] = strdup(optarg);
00245             break;
00246 
00247             /* o, output */
00248         case 4:
00249         case 5:
00250             output[global.outcnt++] = strdup(optarg);
00251             break;
00252 
00253             /* v, version */
00254         case 6:
00255         case 7:
00256             printf("MJPG Streamer Version: %s\n" \
00257             "Compilation Date.....: %s\n" \
00258             "Compilation Time.....: %s\n",
00259 #ifdef SVN_REV
00260             SVN_REV,
00261 #else
00262             SOURCE_VERSION,
00263 #endif
00264             __DATE__, __TIME__);
00265             return 0;
00266             break;
00267 
00268             /* b, background */
00269         case 8:
00270         case 9:
00271             daemon = 1;
00272             break;
00273 
00274         default:
00275             help(argv[0]);
00276             exit(EXIT_FAILURE);
00277         }
00278     }
00279 
00280     openlog("MJPG-streamer ", LOG_PID | LOG_CONS, LOG_USER);
00281     //openlog("MJPG-streamer ", LOG_PID|LOG_CONS|LOG_PERROR, LOG_USER);
00282     syslog(LOG_INFO, "starting application");
00283 
00284     /* fork to the background */
00285     if(daemon) {
00286         LOG("enabling daemon mode");
00287         daemon_mode();
00288     }
00289 
00290     /* ignore SIGPIPE (send by OS if transmitting to closed TCP sockets) */
00291     signal(SIGPIPE, SIG_IGN);
00292 
00293     /* register signal handler for <CTRL>+C in order to clean up */
00294     if(signal(SIGINT, signal_handler) == SIG_ERR) {
00295         LOG("could not register signal handler\n");
00296         closelog();
00297         exit(EXIT_FAILURE);
00298     }
00299 
00300     /*
00301      * messages like the following will only be visible on your terminal
00302      * if not running in daemon mode
00303      */
00304 #ifdef SVN_REV
00305     LOG("MJPG Streamer Version: svn rev: %s\n", SVN_REV);
00306 #else
00307     LOG("MJPG Streamer Version.: %s\n", SOURCE_VERSION);
00308 #endif
00309 
00310     /* check if at least one output plugin was selected */
00311     if(global.outcnt == 0) {
00312         /* no? Then use the default plugin instead */
00313         global.outcnt = 1;
00314     }
00315 
00316     /* open input plugin */
00317     for(i = 0; i < global.incnt; i++) {
00318         /* this mutex and the conditional variable are used to synchronize access to the global picture buffer */
00319         if(pthread_mutex_init(&global.in[i].db, NULL) != 0) {
00320             LOG("could not initialize mutex variable\n");
00321             closelog();
00322             exit(EXIT_FAILURE);
00323         }
00324         if(pthread_cond_init(&global.in[i].db_update, NULL) != 0) {
00325             LOG("could not initialize condition variable\n");
00326             closelog();
00327             exit(EXIT_FAILURE);
00328         }
00329 
00330         tmp = (size_t)(strchr(input[i], ' ') - input[i]);
00331         global.in[i].stop      = 0;
00332         global.in[i].buf       = NULL;
00333         global.in[i].size      = 0;
00334         global.in[i].plugin = (tmp > 0) ? strndup(input[i], tmp) : strdup(input[i]);
00335         global.in[i].handle = dlopen(global.in[i].plugin, RTLD_LAZY);
00336         if(!global.in[i].handle) {
00337             LOG("ERROR: could not find input plugin\n");
00338             LOG("       Perhaps you want to adjust the search path with:\n");
00339             LOG("       # export LD_LIBRARY_PATH=/path/to/plugin/folder\n");
00340             LOG("       dlopen: %s\n", dlerror());
00341             closelog();
00342             exit(EXIT_FAILURE);
00343         }
00344         global.in[i].init = dlsym(global.in[i].handle, "input_init");
00345         if(global.in[i].init == NULL) {
00346             LOG("%s\n", dlerror());
00347             exit(EXIT_FAILURE);
00348         }
00349         global.in[i].stop = dlsym(global.in[i].handle, "input_stop");
00350         if(global.in[i].stop == NULL) {
00351             LOG("%s\n", dlerror());
00352             exit(EXIT_FAILURE);
00353         }
00354         global.in[i].run = dlsym(global.in[i].handle, "input_run");
00355         if(global.in[i].run == NULL) {
00356             LOG("%s\n", dlerror());
00357             exit(EXIT_FAILURE);
00358         }
00359         /* try to find optional command */
00360         global.in[i].cmd = dlsym(global.in[i].handle, "input_cmd");
00361 
00362         global.in[i].param.parameters = strchr(input[i], ' ');
00363 
00364         for (j = 0; j<MAX_PLUGIN_ARGUMENTS; j++) {
00365             global.in[i].param.argv[j] = NULL;
00366         }
00367 
00368         split_parameters(global.in[i].param.parameters, &global.in[i].param.argc, global.in[i].param.argv);
00369         global.in[i].param.global = &global;
00370         global.in[i].param.id = i;
00371 
00372         if(global.in[i].init(&global.in[i].param, i)) {
00373             LOG("input_init() return value signals to exit\n");
00374             closelog();
00375             exit(0);
00376         }
00377     }
00378 
00379     /* open output plugin */
00380     for(i = 0; i < global.outcnt; i++) {
00381         tmp = (size_t)(strchr(output[i], ' ') - output[i]);
00382         global.out[i].plugin = (tmp > 0) ? strndup(output[i], tmp) : strdup(output[i]);
00383         global.out[i].handle = dlopen(global.out[i].plugin, RTLD_LAZY);
00384         if(!global.out[i].handle) {
00385             LOG("ERROR: could not find output plugin %s\n", global.out[i].plugin);
00386             LOG("       Perhaps you want to adjust the search path with:\n");
00387             LOG("       # export LD_LIBRARY_PATH=/path/to/plugin/folder\n");
00388             LOG("       dlopen: %s\n", dlerror());
00389             closelog();
00390             exit(EXIT_FAILURE);
00391         }
00392         global.out[i].init = dlsym(global.out[i].handle, "output_init");
00393         if(global.out[i].init == NULL) {
00394             LOG("%s\n", dlerror());
00395             exit(EXIT_FAILURE);
00396         }
00397         global.out[i].stop = dlsym(global.out[i].handle, "output_stop");
00398         if(global.out[i].stop == NULL) {
00399             LOG("%s\n", dlerror());
00400             exit(EXIT_FAILURE);
00401         }
00402         global.out[i].run = dlsym(global.out[i].handle, "output_run");
00403         if(global.out[i].run == NULL) {
00404             LOG("%s\n", dlerror());
00405             exit(EXIT_FAILURE);
00406         }
00407 
00408         /* try to find optional command */
00409         global.out[i].cmd = dlsym(global.out[i].handle, "output_cmd");
00410 
00411         global.out[i].param.parameters = strchr(output[i], ' ');
00412 
00413         for (j = 0; j<MAX_PLUGIN_ARGUMENTS; j++) {
00414             global.out[i].param.argv[j] = NULL;
00415         }
00416         split_parameters(global.out[i].param.parameters, &global.out[i].param.argc, global.out[i].param.argv);
00417 
00418         global.out[i].param.global = &global;
00419         global.out[i].param.id = i;
00420         if(global.out[i].init(&global.out[i].param, i)) {
00421             LOG("output_init() return value signals to exit\n");
00422             closelog();
00423             exit(EXIT_FAILURE);
00424         }
00425     }
00426 
00427     /* start to read the input, push pictures into global buffer */
00428     DBG("starting %d input plugin\n", global.incnt);
00429     for(i = 0; i < global.incnt; i++) {
00430         syslog(LOG_INFO, "starting input plugin %s", global.in[i].plugin);
00431         if(global.in[i].run(i)) {
00432             LOG("can not run input plugin %d: %s\n", i, global.in[i].plugin);
00433             closelog();
00434             return 1;
00435         }
00436     }
00437 
00438     DBG("starting %d output plugin(s)\n", global.outcnt);
00439     for(i = 0; i < global.outcnt; i++) {
00440         syslog(LOG_INFO, "starting output plugin: %s (ID: %02d)", global.out[i].plugin, global.out[i].param.id);
00441         global.out[i].run(global.out[i].param.id);
00442     }
00443 
00444     /* wait for signals */
00445     pause();
00446 
00447     return 0;
00448 }


corobot_pantilt
Author(s): Morgan Cormier/mcormier@coroware.com
autogenerated on Tue Jan 7 2014 11:39:02