chiark / gitweb /
disorder setup-guest enforces mail_sender requirement
[disorder] / clients / playrtp.c
index 9757be6566d6835da080646181a0799e6a72a369..484d265110470dc7fd9b200f874ee9e41ec50e4b 100644 (file)
@@ -69,6 +69,8 @@
 #include <sys/time.h>
 #include <sys/un.h>
 #include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
 
 #include "log.h"
 #include "mem.h"
@@ -190,6 +192,27 @@ HEAP_DEFINE(pheap, struct packet *, lt_packet);
 /** @brief Control socket or NULL */
 const char *control_socket;
 
+/** @brief Buffer for debugging dump
+ *
+ * The debug dump is enabled by the @c --dump option.  It records the last 20s
+ * of audio to the specified file (which will be about 3.5Mbytes).  The file is
+ * written as as ring buffer, so the start point will progress through it.
+ *
+ * Use clients/dump2wav to convert this to a WAV file, which can then be loaded
+ * into (e.g.) Audacity for further inspection.
+ *
+ * All three backends (ALSA, OSS, Core Audio) now support this option.
+ *
+ * The idea is to allow the user a few seconds to react to an audible artefact.
+ */
+int16_t *dump_buffer;
+
+/** @brief Current index within debugging dump */
+size_t dump_index;
+
+/** @brief Size of debugging dump in samples */
+size_t dump_size = 44100/*Hz*/ * 2/*channels*/ * 20/*seconds*/;
+
 static const struct option options[] = {
   { "help", no_argument, 0, 'h' },
   { "version", no_argument, 0, 'V' },
@@ -208,6 +231,7 @@ static const struct option options[] = {
 #if HAVE_COREAUDIO_AUDIOHARDWARE_H
   { "core-audio", no_argument, 0, 'c' },
 #endif
+  { "dump", required_argument, 0, 'r' },
   { "socket", required_argument, 0, 's' },
   { "config", required_argument, 0, 'C' },
   { 0, 0, 0, 0 }
@@ -509,7 +533,7 @@ static void help(void) {
 
 /* display version number and terminate */
 static void version(void) {
-  xprintf("disorder-playrtp version %s\n", disorder_version_string);
+  xprintf("%s", disorder_version_string);
   xfclose(stdout);
   exit(0);
 }
@@ -532,6 +556,7 @@ int main(int argc, char **argv) {
     struct sockaddr_in6 in6;
   };
   union any_sockaddr mgroup;
+  const char *dumpfile = 0;
 
   static const struct addrinfo prefs = {
     AI_PASSIVE,
@@ -546,7 +571,7 @@ int main(int argc, char **argv) {
 
   mem_init();
   if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
-  while((n = getopt_long(argc, argv, "hVdD:m:b:x:L:R:M:aocC:", options, 0)) >= 0) {
+  while((n = getopt_long(argc, argv, "hVdD:m:b:x:L:R:M:aocC:r", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
     case 'V': version();
@@ -568,6 +593,7 @@ int main(int argc, char **argv) {
 #endif
     case 'C': configfile = optarg; break;
     case 's': control_socket = optarg; break;
+    case 'r': dumpfile = optarg; break;
     default: fatal(0, "invalid option");
     }
   }
@@ -676,6 +702,27 @@ int main(int argc, char **argv) {
     if((err = pthread_create(&tid, 0, control_thread, 0)))
       fatal(err, "pthread_create control_thread");
   }
+  if(dumpfile) {
+    int fd;
+    unsigned char buffer[65536];
+    size_t written;
+
+    if((fd = open(dumpfile, O_RDWR|O_TRUNC|O_CREAT, 0666)) < 0)
+      fatal(errno, "opening %s", dumpfile);
+    /* Fill with 0s to a suitable size */
+    memset(buffer, 0, sizeof buffer);
+    for(written = 0; written < dump_size * sizeof(int16_t);
+        written += sizeof buffer) {
+      if(write(fd, buffer, sizeof buffer) < 0)
+        fatal(errno, "clearing %s", dumpfile);
+    }
+    /* Map the buffer into memory for convenience */
+    dump_buffer = mmap(0, dump_size * sizeof(int16_t), PROT_READ|PROT_WRITE,
+                       MAP_SHARED, fd, 0);
+    if(dump_buffer == (void *)-1)
+      fatal(errno, "mapping %s", dumpfile);
+    info("dumping to %s", dumpfile);
+  }
   play_rtp();
   return 0;
 }