00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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>
00040 #include <linux/videodev2.h>
00041
00042 #include "utils.h"
00043 #include "mjpg_streamer.h"
00044
00045
00046 static globals global;
00047
00048
00049
00050
00051
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
00087
00088
00089
00090
00091
00092
00093 void signal_handler(int sig)
00094 {
00095 int i, j;
00096
00097
00098 LOG("setting signal to stop\n");
00099 global.stop = 1;
00100 usleep(1000 * 1000);
00101
00102
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
00107
00108
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
00118
00119
00120
00121 }
00122 usleep(1000 * 1000);
00123
00124
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;
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
00189
00190
00191
00192 int main(int argc, char *argv[])
00193 {
00194
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
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
00225 if(c == -1) break;
00226
00227
00228 if(c == '?') {
00229 help(argv[0]);
00230 return 0;
00231 }
00232
00233 switch(option_index) {
00234
00235 case 0:
00236 case 1:
00237 help(argv[0]);
00238 return 0;
00239 break;
00240
00241
00242 case 2:
00243 case 3:
00244 input[global.incnt++] = strdup(optarg);
00245 break;
00246
00247
00248 case 4:
00249 case 5:
00250 output[global.outcnt++] = strdup(optarg);
00251 break;
00252
00253
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
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
00282 syslog(LOG_INFO, "starting application");
00283
00284
00285 if(daemon) {
00286 LOG("enabling daemon mode");
00287 daemon_mode();
00288 }
00289
00290
00291 signal(SIGPIPE, SIG_IGN);
00292
00293
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
00302
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
00311 if(global.outcnt == 0) {
00312
00313 global.outcnt = 1;
00314 }
00315
00316
00317 for(i = 0; i < global.incnt; i++) {
00318
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
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
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
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
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
00445 pause();
00446
00447 return 0;
00448 }