OpenWrt lightweight streamer for OSS (/dev/dsp) uncompressed sound data.
authorRoman Bazalevsky <rvb@rvb.name>
Fri, 11 Jul 2014 11:30:35 +0000 (15:30 +0400)
committerRoman Bazalevsky <rvb@rvb.name>
Fri, 11 Jul 2014 11:30:35 +0000 (15:30 +0400)
etc/init.d/oss-streamer [new file with mode: 0644]
oss-streamer.c [new file with mode: 0644]

diff --git a/etc/init.d/oss-streamer b/etc/init.d/oss-streamer
new file mode 100644 (file)
index 0000000..9f0baf3
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2009-2013 OpenWrt.org
+
+START=90
+STOP=10
+
+USE_PROCD=1
+PROG=/usr/bin/oss-streamer
+
+error() {
+       echo "${initscript}:" "$@" 1>&2
+}
+
+start_instance() {
+       procd_open_instance
+       procd_set_param command "$PROG" -p 8081 -d /dev/dsp
+       procd_close_instance
+}
+
+start_service() {
+       start_instance 'oss-streamer'
+}
+
+service_triggers() {
+       procd_add_reload_trigger 'oss-streamer'
+}
diff --git a/oss-streamer.c b/oss-streamer.c
new file mode 100644 (file)
index 0000000..c5702d4
--- /dev/null
@@ -0,0 +1,336 @@
+
+#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;
+}