www.pudn.com > webcam_server-0.50.rar > webcam_server.c
/********************************************************************* * webcam_server * * * * (c) 2002 Donn Morrison donn@donn.dyndns.org * * * * code used from Gerd Knorr's xawtv (libng) * * - and - * * Cory Lueninghoener's gqcam * * * * waits for connections from a viewer and sends * * jpeg encoded captures as a live video feed * * * *********************************************************************/ #include#include #include #include #include #include #include #include #include #include #include #include #include #include "webcam_server.h" #include "grabber.h" //#include "watchdog.h" #include "client.h" #include "version.h" #include "camera.h" #include "unpalette.h" #define DEBUG 0 void print_rgb(char * hdr, struct RGB x) { printf("rgb:%s = %d,%d,%d\n", hdr,x.r&0xFF,x.g&0xFF,x.b&0xFF); } /*----------------------------------------------------------- default options -----------------------------------------------------------*/ #define DEF_PORT 8888 #define DEF_QUALITY 60 #define DEF_CAMDEVFILE "/dev/video0" #define DEF_LOGFILE "/var/log/webcam_server.log" #define DEF_CAPTION "%Y-%m-%d %H:%M:%S" /* strftime */ #define DEF_WIDTH 320 #define DEF_HEIGHT 240 #define DEF_RETRYINIT 0 #define DEF_DAEMON 0 #define DEF_SWAPRGB 0 #define DEF_TESTFPS 0 #define DEF_FLIP_HORIZ 0 #define DEF_FLIP_VERT 0 #define DEF_GAMMA 0 #define DEF_TEXT_FG {0,0,0} #define DEF_TEXT_BG {255,255,255} #define DEF_TRANS {128,0,128} #define DEF_TEXT_XPOS 0 #define DEF_TEXT_YPOS 0 #define DEF_ROTATE 0 #define DEF_FORCE_READ 0 #define DEF_ALLOW_HTTP 1 #define DEF_ALLOW_STREAM 1 #define DEF_MAX_STREAM_BYTES 0 #define DEF_MAX_STREAM_SECONDS 0 #define DEF_MAX_STREAM_FRAMES 0 /* ** currently not implemented #define DEF_ALLOW_ADMIN 0 #define DEF_ADMIN_PW "\0" */ #define DEF_PALETTE -1 char *str_replace(const char *str, const char *find, const char *replace) { char *ptr,*ret; int found=0; ptr = (char*)str; while((ptr=strstr(ptr, find))) { char *start,*end; int slen,elen,rlen,flen; rlen = strlen(replace); flen = strlen(find); slen = ptr - str; elen = strlen(ptr)-flen; start = (char*)malloc(slen+1); strncpy(start, str, slen); start[slen]=0; end = (char*)malloc(elen+1); strncpy(end, ptr+flen, elen); end[elen]=0; ret = (char*)malloc(slen+elen+rlen+1); sprintf(ret, "%s%s%s", start, replace, end); free(start); free(end); ptr = ret; found=1; } if(!found) { ret = (char*)malloc(strlen(str)+1); strcpy(ret, str); } return ret; } /*----------------------------------------------------------- create_and_listen - creates a server socket and listens - its a seperate function to clean up main -----------------------------------------------------------*/ int create_and_listen(int port) { int sockfd, yes=1; struct sockaddr_in addr; if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return -1; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_ANY); if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) { perror("setsockopt"); return -1; } if(bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) < 0) { perror("bind"); return -1; } if(listen(sockfd, 5) < 0) { perror("listen"); return -1; } return sockfd; } /*----------------------------------------------------------- log - opens a logfile for appending on first call - subsequent calls write to the open logfile and/or stdout -----------------------------------------------------------*/ void log(struct caminfo *cam, char *text) { static int tried = 0; static int dups = 0; time_t t; struct tm *tm; char date[128]; static char *last_msg = NULL; if(!tried) { /* open log file */ cam->log = fopen(cam->o.logfile, "a"); if(cam->log == NULL) { fprintf(stderr, "could not open logfile '%s' for writing\n", cam->o.logfile); } tried = 1; } time(&t); tm = localtime(&t); strftime(date,127,"%Y-%m-%d %H:%M:%S",tm); if(last_msg) { if(strcmp(last_msg, text) == 0) { dups++; return; } else { free(last_msg); last_msg = NULL; } } /* write the message to stdout and/or logfile */ pthread_mutex_lock(&cam->lock_log); if(cam->o.verbose) { if(dups > 0) { fprintf(stdout, "%s [%s] log: the last message repeated %d times\n", date, cam->o.devfile, dups); } fprintf(stdout, "%s [%s] %s", date, cam->o.devfile, text); fflush(stdout); } if(cam->log) { if(dups > 0) { fprintf(cam->log, "%s [%s] log: the last message repeated %d times\n", date, cam->o.devfile, dups); } fprintf(cam->log, "%s [%s] %s", date, cam->o.devfile, text); fflush(cam->log); } pthread_mutex_unlock(&cam->lock_log); if(!last_msg) { last_msg = (char *)malloc(strlen(text)+1); strcpy(last_msg, text); dups = 0; } } /*----------------------------------------------------------- usage, parse_args - highly complex, undocumented functions used for encrypting and secretly displaying or parsing various portions of text -----------------------------------------------------------*/ void usage() { printf("webcam_server %s, copyright 2001-2004 Donn Morrison\n\n", get_version()); printf("usage: webcam_server [options]\n\n\ option description [default value]\n\n\ -a - test mode (fps) [no]\n\ -c \"caption\" - caption text [%%Y-%%m-%%d %%H:%%M:%%S], \"\" for none\n\ -d video_device - v4l compliant device [/dev/video]\n\ -Ds - disable stream support [no]\n\ -Dh - disable http support [no]\n\ -fh - flip image horizontally\n\ -fv - flip image vertically\n\ -g geometry - change default geometry (WIDTHxHEIGHT) [320x240]\n\ -G gamma - gamma correction (-100-100) [0]\n\ -h - this help screen\n\ -l logfile - log to file [/var/log/webcam_server.log]\n\ -mb - maximum bytes per connection [0 (disabled)]\n\ -ms - maximum seconds per connection [0 (disabled)]\n\ -mf - maximum frames per connection [0 (disabled)]\n\ -n - force use of read() system call, rather than mmap\n\ -p tcp_port - tcp listen port [8888]\n\ -q jpeg_quality - jpeg compression quality (0-100) [60]\n\ -r - if camera init fails, retry forever [no]\n\ -R value - rotate image value*90 degrees CCW (0-3) [0]\n\ -s - daemon mode [no]\n\ -tf R,G,B - text forground colour [255,255,255]\n\ -tb R,G,B - text background colour [0,0,0]\n\ -tt R,G,B - transparency colour [128,0,128]\n\ (set -tf or -tb to this to make transparent)\n\ -T x,y - xy-position of text [0,0]\n\ -v - verbose [no]\n\ -x - swap RGB -> BGR [no]\n\ \n"); /* ** currently not implemented -Ea - enable admin support [no]\n\ -e password - set admin password [null]\n\ -P - force palette n (videodev.h) [off]\n\ */ } int parse_args(struct caminfo *cam, int argc, char *argv[]) { int res = 0, i; for(i=1; i o.port = atoi(argv[++i]); else res = -1; break; case 'd': if(i+1 < argc) cam->o.devfile = argv[++i]; else res = -1; break; case 'c': if(i+1 < argc) cam->o.caption = argv[++i]; else res = -1; break; case 'v': cam->o.verbose = 1; break; case 'r': cam->o.retry_init = 1; break; case 's': cam->o.daemon = 1; break; case 'q': if(i+1 < argc) cam->o.jpeg_quality = atoi(argv[++i]); else res = -1; break; /* case 'P': if(i+1 < argc) cam->o.palette = atoi(argv[++i]); else res = -1; break; */ case 'x': cam->o.swap_rgb = 1; break; case 'f': switch(argv[i][2]) { case 'h': cam->o.flip_horiz = 1; break; case 'v': cam->o.flip_vert = 1; break; } break; case 'T': { if(i+1 < argc) { int x,y; sscanf(argv[++i], "%d,%d", &x,&y); cam->o.text_xpos = x; cam->o.text_ypos = y; printf("text xy = %d,%d\n", x,y); } else res = -1; } break; case 't': switch(argv[i][2]) { case 'f': if(i+1 < argc) { int r,g,b; sscanf(argv[++i], "%d,%d,%d", &r, &g, &b); cam->o.text_fg.r = r; cam->o.text_fg.g = g; cam->o.text_fg.b = b; } else res = -1; break; case 'b': if(i+1 < argc) { int r,g,b; sscanf(argv[++i], "%d,%d,%d", &r, &g, &b); cam->o.text_bg.r = r; cam->o.text_bg.g = g; cam->o.text_bg.b = b; } else res = -1; break; case 't': if(i+1 < argc) { int r,g,b; sscanf(argv[++i], "%d,%d,%d", &r, &g, &b); cam->o.trans.r = r; cam->o.trans.g = g; cam->o.trans.b = b; } else res = -1; break; default: res = -1; } break; case 'D': switch(argv[i][2]) { case 'h': cam->o.allow_http = 0; break; case 's': cam->o.allow_stream = 0; break; default: res = -1; } break; /* ** currently not implemented case 'E': switch(argv[i][2]) { case 'a': cam->o.allow_admin = 1; break; default: res = -1; } break; case 'e': if(i+1 < argc) cam->o.admin_pw = argv[++i]; else res = -1; break; */ case 'm': if(i+1 < argc) switch(argv[i][2]) { case 'b': cam->o.max_stream_bytes = atoi(argv[++i]); break; case 's': cam->o.max_stream_seconds = atoi(argv[++i]); break; case 'f': cam->o.max_stream_frames = atoi(argv[++i]); break; default: res = -1; } else res = -1; break; case 'a': cam->o.test_fps = 1; break; case 'n': cam->o.use_read = 1; break; case 'l': if(i+1 < argc) cam->o.logfile = argv[++i]; else res = -1; break; case 'R': if(i+1 < argc) cam->o.rotate = atoi(argv[++i]); else res = -1; break; case 'g': if(i+1 < argc) { int w=0,h=0; if(strchr(argv[i+1], 'x') == NULL) { res = -1; break; } sscanf(argv[++i], "%dx%d", &w, &h); if(w < 1 || h < 1 || w > 2048 || h > 2048) { fprintf(stderr, "invalid width, height: %dx%d. using defaults: %dx%d\n", w,h,DEF_WIDTH,DEF_HEIGHT); cam->o.width = DEF_WIDTH; cam->o.height = DEF_HEIGHT; } cam->o.width = w; cam->o.height = h; } else res = -1; break; case 'G': if(i+1 < argc) { cam->o.gamma = atoi(argv[++i]); } else res = -1; break; default: res = -1; break; }; } else { res = -1; } if(res == -1) { fprintf(stderr, "invalid option or missing parameter: %s\n", argv[i]); return res; } } return res; } int main(int argc, char *argv[]) { int sockfd=0, clientfd; struct sockaddr_in remote_addr; socklen_t addr_len; pthread_t pt; struct caminfo *cam; struct connection *con; int res; cam = (struct caminfo *)malloc(sizeof(struct caminfo)); if(!cam) { fprintf(stderr, "malloc error\n"); exit(1); } cam->mmap = NULL; /* ignore broken pipes n stuff */ signal(SIGPIPE, SIG_IGN); signal(SIGALRM, SIG_IGN); /* init mutexes */ pthread_mutex_init(&cam->lock_queue, NULL); pthread_mutex_init(&cam->lock_info, NULL); pthread_mutex_init(&cam->lock_id, NULL); pthread_mutex_init(&cam->lock_log, NULL); sem_init(&cam->sem_con, 0, 0); /* defaults */ cam->o.caption = DEF_CAPTION; cam->o.devfile = DEF_CAMDEVFILE; cam->o.port = DEF_PORT; cam->o.jpeg_quality = DEF_QUALITY; cam->o.retry_init = DEF_RETRYINIT; cam->o.daemon = DEF_DAEMON; cam->o.swap_rgb = DEF_SWAPRGB; cam->o.test_fps = DEF_TESTFPS; cam->o.width = DEF_WIDTH; cam->o.height = DEF_HEIGHT; cam->o.logfile = DEF_LOGFILE; cam->o.flip_horiz = DEF_FLIP_HORIZ; cam->o.flip_vert = DEF_FLIP_VERT; cam->o.gamma = DEF_GAMMA; struct RGB fg = DEF_TEXT_FG; struct RGB bg = DEF_TEXT_BG; struct RGB trans = DEF_TRANS; cam->o.text_fg = fg; cam->o.text_bg = bg; cam->o.trans = trans; cam->o.text_xpos = DEF_TEXT_XPOS; cam->o.text_ypos = DEF_TEXT_YPOS; cam->o.rotate = DEF_ROTATE; cam->o.use_read = DEF_FORCE_READ; cam->o.allow_http = DEF_ALLOW_HTTP; cam->o.allow_stream = DEF_ALLOW_STREAM; cam->o.max_stream_bytes = DEF_MAX_STREAM_BYTES; cam->o.max_stream_seconds = DEF_MAX_STREAM_SECONDS; cam->o.max_stream_frames = DEF_MAX_STREAM_FRAMES; /* ** currently not implemented cam->o.admin_pw = DEF_ADMIN_PW; cam->o.allow_admin = DEF_ALLOW_ADMIN; */ cam->o.palette = DEF_PALETTE; /* overwrite defaults with command line args */ if(parse_args(cam, argc, argv) < 0) exit(1); /* don't do this stuff if we're in test mode */ if(!cam->o.test_fps) { /* create listening socket */ sockfd = create_and_listen(cam->o.port); if(sockfd < 1) exit(1); /* go into daemon mode if told to */ if(cam->o.daemon) if(daemon(0,0) < 0) { fprintf(stderr, "daemon failed.\n"); exit(1); } } /* open camera device...retry infinitely if told to */ res = open_cam(cam, cam->o.devfile); while(res < 0 && cam->o.retry_init) { usleep(5000000); fprintf(stderr, "camera init failed, retrying...\n"); res = open_cam(cam, cam->o.devfile); } if(res < 0) { fprintf(stderr, "unable to initialise camera\n"); exit(1); } /* get camera defaults */ get_cam_info(cam); cam->vid_win.width = cam->o.width; cam->vid_win.height = cam->o.height; if(set_cam_info(cam) < 0) { fprintf(stderr, "error setting video device parameters, using defaults\n"); get_cam_info(cam); if(set_cam_info(cam) < 0) { fprintf(stderr, "not a valid video device? quitting.\n"); exit(1); } } cam->o.width = cam->vid_win.width; cam->o.height = cam->vid_win.height; { /*if(cam->o.palette != -1) { /* force use of certain palette */ /* cam->vid_pic.palette = cam->o.palette; ioctl(cam->dev, VIDIOCSPICT, &cam->vid_pic); ioctl(cam->dev, VIDIOCGPICT, &cam->vid_pic); if(cam->vid_pic.palette == cam->o.palette) { printf("using palette %d\n", cam->o.palette); } else { printf("could not set palette to %d\n", cam->o.palette); } } else { */ /* check for a valid colour palette */ int palfound = 0; int brightness = -1, hue = -1, colour = -1, contrast = -1, whiteness = -1; for(cam->pal = palettes; cam->pal->val >= 0; cam->pal++) { if(cam->o.palette != -1) { if(cam->pal->val != cam->o.palette) continue; else cam->vid_pic.palette = cam->pal->val; } else { cam->vid_pic.palette = cam->pal->val; } cam->vid_pic.depth = cam->pal->depth; if(brightness >= 0) cam->vid_pic.brightness = brightness; if(hue >= 0) cam->vid_pic.hue = hue; if(colour >= 0) cam->vid_pic.colour = colour; if(contrast >= 0) cam->vid_pic.contrast = contrast; if(whiteness >= 0) cam->vid_pic.whiteness = whiteness; ioctl(cam->dev, VIDIOCSPICT, &cam->vid_pic); ioctl(cam->dev, VIDIOCGPICT, &cam->vid_pic); if(cam->vid_pic.palette == cam->pal->val) { // printf("using palette %d\n", cam->pal->val); palfound = 1; break; } } if(!palfound) { fprintf(stderr, "No supported colour palette found.\n"); return 1; } } cam->grab_thread_alive = 1; pthread_create(&cam->pt_grab, NULL, (void*)grab_thread, (void *)cam); /* start the watchdog thread */ /* pthread_create(&pt, NULL, (void*)watchdog_thread, (void *)cam); pthread_detach(pt); */ if(cam->o.test_fps) { pthread_join(cam->pt_grab, NULL); exit(0); } else { pthread_detach(cam->pt_grab); } log(cam, "webcam_server started\n"); addr_len = sizeof(remote_addr); while((clientfd = accept(sockfd, (struct sockaddr *)&remote_addr, &addr_len)) > 0) { /* con is free'd at end of handle_connection */ con = (struct connection*)malloc(sizeof(struct connection)); con->cam = cam; /* get connection info */ con->socketfd = clientfd; memcpy(&con->remote_addr, &remote_addr, addr_len); /* handle the connection */ pthread_create(&pt, NULL, (void*)handle_connection, (void*)con); pthread_detach(pt); } close(sockfd); return 0; }