X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/6fba990cea5342a2101b4acceb5c9ee02cb87c92..5aff007d8fcfb4c6cc3c3627ae15f45562db7a0d:/clients/playrtp.c diff --git a/clients/playrtp.c b/clients/playrtp.c index 9757be6..31416ec 100644 --- a/clients/playrtp.c +++ b/clients/playrtp.c @@ -1,6 +1,6 @@ /* * This file is part of DisOrder. - * Copyright (C) 2007 Richard Kettlewell + * Copyright (C) 2007, 2008 Richard Kettlewell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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 } @@ -305,8 +330,9 @@ static void *queue_thread(void attribute((unused)) *arg) { for(;;) { /* Get the next packet */ pthread_mutex_lock(&receive_lock); - while(!received_packets) + while(!received_packets) { pthread_cond_wait(&receive_cond, &receive_lock); + } p = received_packets; received_packets = p->next; if(!received_packets) @@ -404,8 +430,9 @@ static void *listen_thread(void attribute((unused)) *arg) { * out of order then we guarantee dropouts. But for now... */ if(nsamples >= maxbuffer) { pthread_mutex_lock(&lock); - while(nsamples >= maxbuffer) + while(nsamples >= maxbuffer) { pthread_cond_wait(&cond, &lock); + } pthread_mutex_unlock(&lock); } /* Add the packet to the receive queue */ @@ -428,8 +455,9 @@ void playrtp_fill_buffer(void) { while(nsamples) drop_first_packet(); info("Buffering..."); - while(nsamples < readahead) + while(nsamples < readahead) { pthread_cond_wait(&cond, &lock); + } next_timestamp = pheap_first(&packets)->timestamp; active = 1; } @@ -507,13 +535,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 +553,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 +568,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 +590,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 +699,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; }