X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/4ecbdbd99dea3236c3c6d5ea5401a08c56de5d3c..30d030b163ea4224ff2eaaf7708d794d76b93cee:/clients/playrtp.c diff --git a/clients/playrtp.c b/clients/playrtp.c index 9757be6..4f6e57a 100644 --- a/clients/playrtp.c +++ b/clients/playrtp.c @@ -69,6 +69,8 @@ #include #include #include +#include +#include #include "log.h" #include "mem.h" @@ -83,6 +85,7 @@ #include "client.h" #include "playrtp.h" #include "inputline.h" +#include "version.h" #define readahead linux_headers_are_borked @@ -190,6 +193,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 +232,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 } @@ -507,13 +532,6 @@ static void help(void) { exit(0); } -/* display version number and terminate */ -static void version(void) { - xprintf("disorder-playrtp version %s\n", disorder_version_string); - xfclose(stdout); - exit(0); -} - int main(int argc, char **argv) { int n, err; struct addrinfo *res; @@ -532,6 +550,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,10 +565,10 @@ 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(); + case 'V': version("disorder-playrtp"); case 'd': debugging = 1; break; case 'D': device = optarg; break; case 'm': minbuffer = 2 * atol(optarg); break; @@ -568,6 +587,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 +696,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; }