+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <pthread.h>
+#include <fcntl.h>
+
+#define SOURCE_VERSION "0.0"
+#define BOUNDARY "arflebarfle"
+
+typedef enum { SNAPSHOT, STREAM } answer_t;
+
+/* globals */
+int stop=0, sd;
+int force_delay=0;
+#define BUF_SIZE 4096
+int source_dev;
+
+/* signal fresh frames */
+pthread_mutex_t db = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t db_update = PTHREAD_COND_INITIALIZER;
+
+/* global JPG frame, this is more or less the "database" */
+char g_buf[BUF_SIZE];
+
+/* thread for clients that connected to this server */
+void *client_thread( void *arg ) {
+ int fd = *((int *)arg);
+ fd_set fds;
+ char frame[BUF_SIZE];
+ int ok = 1;
+ char buffer[1024] = {0};
+ struct timeval to;
+
+ if (arg!=NULL) free(arg); else exit(1);
+
+ /* set timeout to 5 seconds */
+ to.tv_sec = 5;
+ to.tv_usec = 0;
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ if( select(fd+1, &fds, NULL, NULL, &to) <= 0) {
+ close(fd);
+ return NULL;
+ }
+
+ /* find out if we should deliver something other than a stream */
+
+ sprintf(buffer, "HTTP/1.0 200 OK\r\n" \
+ "Server: OSS Streamer\r\n" \
+ "Content-Type: audio/wav\r\n" \
+ "Cache-Control: no-cache\r\n" \
+ "Cache-Control: private\r\n" \
+ "Pragma: no-cache\r\n" \
+ "\r\n"
+ );
+
+ ok = ( write(fd, buffer, strlen(buffer)) >= 0)?1:0;
+
+ while ( ok >= 0 && !stop ) {
+
+ /* having a problem with windows (do we not always) browsers not updating the
+ stream display, unless the browser cache is disabled - try and implement a delay
+ to allow movement to end before streem goes on - kind of works, but not well enough */
+
+ /* wait for fresh frames */
+ pthread_cond_wait(&db_update, &db);
+
+ /* read buffer */
+ memcpy(frame, g_buf, BUF_SIZE);
+
+ pthread_mutex_unlock( &db );
+
+ ok = ( write(fd, frame, BUF_SIZE) >= 0)?1:0;
+ if( ok == 0 ) break;
+
+ }
+
+ close(fd);
+
+ return NULL;
+}
+
+/* the single writer thread */
+void *get_thread( void *arg ) {
+
+ char rbuffer[BUF_SIZE];
+
+ while( !stop ) {
+ /* grab a frame */
+ if( read(source_dev,rbuffer,BUF_SIZE) < 0 ) {
+ fprintf(stderr, "Error grabbing\n");
+ exit(1);
+ }
+
+ /* copy frame to global buffer */
+ pthread_mutex_lock( &db );
+
+ memcpy(g_buf, rbuffer, BUF_SIZE);
+
+ /* signal fresh_frame */
+ pthread_cond_broadcast(&db_update);
+ pthread_mutex_unlock( &db );
+
+ /* only use usleep if the fps is below 5, otherwise the overhead is too long */
+ }
+
+ return NULL;
+}
+
+void help(char *progname)
+{
+ fprintf(stderr, "------------------------------------------------------------------\n");
+ fprintf(stderr, "Usage: oss-stream" \
+ " [-h | --help ]........: display this help\n" \
+ " [-d | --device ]......: video device to open (your camera)\n" \
+ " [-p | --port ]........: TCP-port for the server\n" \
+ " [-v | --version ].....: display version information\n" \
+ " [-b | --background]...: fork to the background, daemon mode\n");
+ fprintf(stderr, "------------------------------------------------------------------\n");
+}
+
+void signal_handler(int sigm) {
+ /* signal "stop" to threads */
+ stop = 1;
+
+ /* cleanup most important structures */
+ fprintf(stderr, "shutdown...\n");
+ usleep(1000*1000);
+ if (close (sd) < 0)
+ perror ("close sd");;
+ pthread_cond_destroy(&db_update);
+ pthread_mutex_destroy(&db);
+ exit(0);
+ return;
+}
+
+void daemon_mode(void) {
+ int fr=0;
+
+ fr = fork();
+ if( fr < 0 ) {
+ fprintf(stderr, "fork() failed\n");
+ exit(1);
+ }
+ if ( fr > 0 ) {
+ exit(0);
+ }
+
+ if( setsid() < 0 ) {
+ fprintf(stderr, "setsid() failed\n");
+ exit(1);
+ }
+
+ fr = fork();
+ if( fr < 0 ) {
+ fprintf(stderr, "fork() failed\n");
+ exit(1);
+ }
+ if ( fr > 0 ) {
+ fprintf(stderr, "forked to background (%d)\n", fr);
+ exit(0);
+ }
+
+ umask(0);
+
+ chdir("/");
+ close(0);
+ close(1);
+ close(2);
+
+ open("/dev/null", O_RDWR);
+ dup(0);
+ dup(0);
+}
+
+/* #########################################################################
+Main
+######################################################################### */
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in addr;
+ int on=1;
+ pthread_t client, get;
+ char *dev = "/dev/dsp";
+ unsigned int stream_port = 0;
+ int daemon=0;
+
+ while(1) {
+ int option_index = 0, c=0;
+ static struct option long_options[] = \
+ {
+ {"h", no_argument, 0, 0},
+ {"help", no_argument, 0, 0},
+ {"d", required_argument, 0, 0},
+ {"device", required_argument, 0, 0},
+ {"p", required_argument, 0, 0},
+ {"port", required_argument, 0, 0},
+ {"v", no_argument, 0, 0},
+ {"version", no_argument, 0, 0},
+ {"b", no_argument, 0, 0},
+ {"background", no_argument, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long_only(argc, argv, "", long_options, &option_index);
+
+ /* no more options to parse */
+ if (c == -1) break;
+
+ /* unrecognized option */
+ if(c=='?'){ help(argv[0]); return 0; }
+
+ switch (option_index) {
+ /* h, help */
+ case 0:
+ case 1:
+ help(argv[0]);
+ return 0;
+ break;
+
+ /* d, device */
+ case 2:
+ case 3:
+ dev = strdup(optarg);
+ break;
+
+ /* p, port */
+ case 4:
+ case 5:
+ stream_port=htons(atoi(optarg));
+ break;
+
+ /* v, version */
+ case 6:
+ case 7:
+ printf("OSS Streamer Version: %s\n" \
+ "Compilation Date....: %s\n" \
+ "Compilation Time....: %s\n", SOURCE_VERSION, __DATE__, __TIME__);
+ return 0;
+ break;
+
+ /* b, background */
+ case 8:
+ case 9:
+ daemon=1;
+ break;
+
+ default:
+ help(argv[0]);
+ return 0;
+ }
+ }
+
+ if ( stream_port==0 ) {
+ fprintf(stderr, "server port not set\n");
+ exit(2);
+ }
+
+ /* ignore SIGPIPE (send if transmitting to closed sockets) */
+ signal(SIGPIPE, SIG_IGN);
+ if (signal(SIGINT, signal_handler) == SIG_ERR) {
+ fprintf(stderr, "could not register signal handler\n");
+ exit(1);
+ }
+
+ /* fork to the background */
+ if ( daemon ) {
+ daemon_mode();
+ }
+
+ /* allocate audio datastructure */
+
+ /* open video device and prepare data structure */
+ source_dev = open(dev,O_RDONLY);
+ if (source_dev < 0) {
+ fprintf(stderr, "error opening source device\n");
+ exit(1);
+ }
+
+ /* open socket for server */
+ sd = socket(PF_INET, SOCK_STREAM, 0);
+ if ( sd < 0 ) {
+ fprintf(stderr, "socket failed\n");
+ exit(1);
+ }
+
+ /* ignore "socket already in use" errors */
+ if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
+ perror("setsockopt(SO_REUSEADDR) failed");
+ exit(1);
+ }
+
+ /* configure server address to listen to all local IPs */
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = stream_port;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 ) {
+ fprintf(stderr, "bind failed\n");
+ perror("Bind");
+ exit(1);
+ }
+
+ /* start listening on socket */
+ if ( listen(sd, 10) != 0 ) {
+ fprintf(stderr, "listen failed\n");
+ exit(1);
+ }
+
+ /* start to read the camera, push picture buffers into global buffer */
+ pthread_create(&get, 0, get_thread, NULL);
+ pthread_detach(get);
+
+ /* start motor control server */
+
+ /* create a child for every client that connects */
+ while ( 1 ) {
+ int *pfd = (int *)calloc(1, sizeof(int));
+ *pfd = accept(sd, 0, 0);
+ pthread_create(&client, NULL, &client_thread, pfd);
+ pthread_detach(client);
+
+ }
+
+ return 0;
+}