📄 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 <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <signal.h>#include <string.h>#include <pthread.h>#include <linux/videodev.h>#include <semaphore.h>#include <sys/ioctl.h>#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/*----------------------------------------------------------- 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_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"*//*----------------------------------------------------------- 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,2002 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]\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 (0-3) [0]\n\ -s - daemon mode [no]\n\ -tf colour - text forground colour (0-255) [255]\n\ -v - verbose [no]\n\ -x - swap RGB -> BGR [no]\n\\n");/* ** currently not implemented -tb colour - text background colour (0-255) [0]\n\ -Ea - enable admin support [no]\n\ -e password - set admin password [null]\n\*/}int parse_args(struct caminfo *cam, int argc, char *argv[]){ int res = 0, i; for(i=1; i<argc; i++) { if(argv[i][0] == '-') { switch(argv[i][1]) { case 'h': usage(); return -1; case 'p': if(i+1 < argc) cam->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 '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': switch(argv[i][2]) { case 'f': if(i+1 < argc) { sscanf(argv[++i], "%d,%d,%d", &cam->o.text_fg.r, &cam->o.text_fg.g, &cam->o.text_fg.b); } else res = -1; break; case 'b': if(i+1 < argc) { sscanf(argv[++i], "%d,%d,%d", &cam->o.text_bg.r, &cam->o.text_bg.g, &cam->o.text_bg.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,h; char *arg = argv[++i]; char *ptr; ptr = (char *)strchr(arg, 'x'); if(!ptr) { res = -1; break; } *ptr = 0; // eek! changing the cmd line params ptr++; w = atoi(arg); h = atoi(ptr); if(w < 1 || h < 1) { fprintf(stderr, "invalid width, height: %d,%d. using defaults: %d,%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;// cam->o.text_fg = DEF_TEXT_FG;// cam->o.text_bg = DEF_TEXT_BG; memset(&cam->o.text_fg, 0, 3); memset(&cam->o.text_fg, 255, 3); 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;*/ printf("main pid = %d\n", getpid()); /* 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; { /* 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++) { 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) { palfound = 1; break; } } if(!palfound) { printf("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;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -