~mdw
/
disorder
/ blobdiff
commit
grep
author
committer
pickaxe
?
search:
re
summary
|
shortlog
|
log
|
commit
|
commitdiff
|
tree
raw
|
inline
| side by side
Merge audio timing fix branch.
[disorder]
/
clients
/
playrtp.c
diff --git
a/clients/playrtp.c
b/clients/playrtp.c
index
338cbbf
..
f5f538d
100644
(file)
--- a/
clients/playrtp.c
+++ b/
clients/playrtp.c
@@
-24,20
+24,20
@@
* systems. There is no support for Microsoft Windows yet, and that will in
* fact probably an entirely separate program.
*
* systems. There is no support for Microsoft Windows yet, and that will in
* fact probably an entirely separate program.
*
- * The program runs (at least) three threads. listen_thread() is responsible
- * for reading RTP packets off the wire and adding them to the linked list @ref
- * received_packets, assuming they are basically sound. queue_thread() takes
- * packets off this linked list and adds them to @ref packets (an operation
- * which might be much slower due to contention for @ref lock).
+ * The program runs (at least) three threads:
*
*
- *
The main thread is responsible for actually playing audio. In ALSA this
- *
means it waits until ALSA says it's ready for more audio which it then
- *
plays. See @ref clients/playrtp-alsa.c
.
+ *
listen_thread() is responsible for reading RTP packets off the wire and
+ *
adding them to the linked list @ref received_packets, assuming they are
+ *
basically sound
.
*
*
- * In Core Audio the main thread is only responsible for starting and stopping
- * play: the system does the actual playback in its own private thread, and
- * calls adioproc() to fetch the audio data. See @ref
- * clients/playrtp-coreaudio.c.
+ * queue_thread() takes packets off this linked list and adds them to @ref
+ * packets (an operation which might be much slower due to contention for @ref
+ * lock).
+ *
+ * control_thread() accepts commands from Disobedience (or anything else).
+ *
+ * The main thread activates and deactivates audio playing via the @ref
+ * lib/uaudio.h API (which probably implies at least one further thread).
*
* Sometimes it happens that there is no audio available to play. This may
* because the server went away, or a packet was dropped, or the server
*
* Sometimes it happens that there is no audio available to play. This may
* because the server went away, or a packet was dropped, or the server
@@
-81,8
+81,6
@@
#include "version.h"
#include "uaudio.h"
#include "version.h"
#include "uaudio.h"
-#define readahead linux_headers_are_borked
-
/** @brief Obsolete synonym */
#ifndef IPV6_JOIN_GROUP
# define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
/** @brief Obsolete synonym */
#ifndef IPV6_JOIN_GROUP
# define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
@@
-96,19
+94,13
@@
static FILE *logfp;
/** @brief Output device */
/** @brief Output device */
-/** @brief Minimum low watermark
- *
- * We'll stop playing if there's only this many samples in the buffer. */
-unsigned minbuffer = 2 * 44100 / 10; /* 0.2 seconds */
-
-/** @brief Buffer high watermark
- *
- * We'll only start playing when this many samples are available. */
-static unsigned readahead = 44100; /* 0.5 seconds */
+/** @brief Buffer low watermark in samples */
+unsigned minbuffer = 4 * (2 * 44100) / 10; /* 0.4 seconds */
-/** @brief Maximum buffer size
+/** @brief Maximum buffer size
in samples
*
*
- * We'll stop reading from the network if we have this many samples. */
+ * We'll stop reading from the network if we have this many samples.
+ */
static unsigned maxbuffer;
/** @brief Received packets
static unsigned maxbuffer;
/** @brief Received packets
@@
-204,7
+196,6
@@
static const struct option options[] = {
{ "device", required_argument, 0, 'D' },
{ "min", required_argument, 0, 'm' },
{ "max", required_argument, 0, 'x' },
{ "device", required_argument, 0, 'D' },
{ "min", required_argument, 0, 'm' },
{ "max", required_argument, 0, 'x' },
- { "buffer", required_argument, 0, 'b' },
{ "rcvbuf", required_argument, 0, 'R' },
#if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
{ "oss", no_argument, 0, 'o' },
{ "rcvbuf", required_argument, 0, 'R' },
#if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
{ "oss", no_argument, 0, 'o' },
@@
-440,12
+431,18
@@
static void *listen_thread(void attribute((unused)) *arg) {
* Must be called with @ref lock held.
*/
void playrtp_fill_buffer(void) {
* Must be called with @ref lock held.
*/
void playrtp_fill_buffer(void) {
- while(nsamples)
+ /* Discard current buffer contents */
+ while(nsamples) {
+ //fprintf(stderr, "%8u/%u (%u) DROPPING\n", nsamples, maxbuffer, minbuffer);
drop_first_packet();
drop_first_packet();
+ }
info("Buffering...");
info("Buffering...");
- while(nsamples < readahead) {
+ /* Wait until there's at least minbuffer samples available */
+ while(nsamples < minbuffer) {
+ //fprintf(stderr, "%8u/%u (%u) FILLING\n", nsamples, maxbuffer, minbuffer);
pthread_cond_wait(&cond, &lock);
}
pthread_cond_wait(&cond, &lock);
}
+ /* Start from whatever is earliest */
next_timestamp = pheap_first(&packets)->timestamp;
active = 1;
}
next_timestamp = pheap_first(&packets)->timestamp;
active = 1;
}
@@
-480,7
+477,6
@@
static void help(void) {
"Options:\n"
" --device, -D DEVICE Output device\n"
" --min, -m FRAMES Buffer low water mark\n"
"Options:\n"
" --device, -D DEVICE Output device\n"
" --min, -m FRAMES Buffer low water mark\n"
- " --buffer, -b FRAMES Buffer high water mark\n"
" --max, -x FRAMES Buffer maximum size\n"
" --rcvbuf, -R BYTES Socket receive buffer size\n"
" --config, -C PATH Set configuration file\n"
" --max, -x FRAMES Buffer maximum size\n"
" --rcvbuf, -R BYTES Socket receive buffer size\n"
" --config, -C PATH Set configuration file\n"
@@
-537,8
+533,6
@@
static size_t playrtp_callback(void *buffer,
*bufptr++ = (int16_t)ntohs(*ptr++);
--i;
}
*bufptr++ = (int16_t)ntohs(*ptr++);
--i;
}
- /* We don't junk the packet here; a subsequent call to
- * playrtp_next_packet() will dispose of it (if it's actually done with). */
} else {
/* There is no suitable packet. We introduce 0s up to the next packet, or
* to fill the buffer if there's no next packet or that's too many. The
} else {
/* There is no suitable packet. We introduce 0s up to the next packet, or
* to fill the buffer if there's no next packet or that's too many. The
@@
-559,6
+553,8
@@
static size_t playrtp_callback(void *buffer,
}
/* Advance timestamp */
next_timestamp += samples;
}
/* Advance timestamp */
next_timestamp += samples;
+ /* Junk obsolete packets */
+ playrtp_next_packet();
pthread_mutex_unlock(&lock);
return samples;
}
pthread_mutex_unlock(&lock);
return samples;
}
@@
-568,7
+564,7
@@
int main(int argc, char **argv) {
struct addrinfo *res;
struct stringlist sl;
char *sockname;
struct addrinfo *res;
struct stringlist sl;
char *sockname;
- int rcvbuf, target_rcvbuf =
131072
;
+ int rcvbuf, target_rcvbuf =
0
;
socklen_t len;
struct ip_mreq mreq;
struct ipv6_mreq mreq6;
socklen_t len;
struct ip_mreq mreq;
struct ipv6_mreq mreq6;
@@
-592,17
+588,19
@@
int main(int argc, char **argv) {
.ai_protocol = IPPROTO_UDP
};
.ai_protocol = IPPROTO_UDP
};
+ /* Timing information is often important to debugging playrtp, so we include
+ * timestamps in the logs */
+ logdate = 1;
mem_init();
if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
backend = uaudio_apis[0];
mem_init();
if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
backend = uaudio_apis[0];
- while((n = getopt_long(argc, argv, "hVdD:m:
b:
x:L:R:M:aocC:re:P:", options, 0)) >= 0) {
+ while((n = getopt_long(argc, argv, "hVdD:m:x:L:R:M:aocC:re:P:", options, 0)) >= 0) {
switch(n) {
case 'h': help();
case 'V': version("disorder-playrtp");
case 'd': debugging = 1; break;
case 'D': uaudio_set("device", optarg); break;
case 'm': minbuffer = 2 * atol(optarg); break;
switch(n) {
case 'h': help();
case 'V': version("disorder-playrtp");
case 'd': debugging = 1; break;
case 'D': uaudio_set("device", optarg); break;
case 'm': minbuffer = 2 * atol(optarg); break;
- case 'b': readahead = 2 * atol(optarg); break;
case 'x': maxbuffer = 2 * atol(optarg); break;
case 'L': logfp = fopen(optarg, "w"); break;
case 'R': target_rcvbuf = atoi(optarg); break;
case 'x': maxbuffer = 2 * atol(optarg); break;
case 'L': logfp = fopen(optarg, "w"); break;
case 'R': target_rcvbuf = atoi(optarg); break;
@@
-625,7
+623,7
@@
int main(int argc, char **argv) {
}
if(config_read(0)) fatal(0, "cannot read configuration");
if(!maxbuffer)
}
if(config_read(0)) fatal(0, "cannot read configuration");
if(!maxbuffer)
- maxbuffer =
4 * readahead
;
+ maxbuffer =
2 * minbuffer
;
argc -= optind;
argv += optind;
switch(argc) {
argc -= optind;
argv += optind;
switch(argc) {
@@
-740,6
+738,7
@@
int main(int argc, char **argv) {
rcvbuf, target_rcvbuf);
} else
info("default socket receive buffer %d", rcvbuf);
rcvbuf, target_rcvbuf);
} else
info("default socket receive buffer %d", rcvbuf);
+ //info("minbuffer %u maxbuffer %u", minbuffer, maxbuffer);
if(logfp)
info("WARNING: -L option can impact performance");
if(control_socket) {
if(logfp)
info("WARNING: -L option can impact performance");
if(control_socket) {
@@
-791,12
+790,28
@@
int main(int argc, char **argv) {
pthread_mutex_unlock(&lock);
backend->activate();
pthread_mutex_lock(&lock);
pthread_mutex_unlock(&lock);
backend->activate();
pthread_mutex_lock(&lock);
- /* Wait until the buffer empties out */
+ /* Wait until the buffer empties out
+ *
+ * If there's a packet that we can play right now then we definitely
+ * continue.
+ *
+ * Also if there's at least minbuffer samples we carry on regardless and
+ * insert silence. The assumption is there's been a pause but more data
+ * is now available.
+ */
while(nsamples >= minbuffer
|| (nsamples > 0
&& contains(pheap_first(&packets), next_timestamp))) {
while(nsamples >= minbuffer
|| (nsamples > 0
&& contains(pheap_first(&packets), next_timestamp))) {
+ //fprintf(stderr, "%8u/%u (%u) PLAYING\n", nsamples, maxbuffer, minbuffer);
pthread_cond_wait(&cond, &lock);
}
pthread_cond_wait(&cond, &lock);
}
+#if 0
+ if(nsamples) {
+ struct packet *p = pheap_first(&packets);
+ fprintf(stderr, "nsamples=%u (%u) next_timestamp=%"PRIx32", first packet is [%"PRIx32",%"PRIx32")\n",
+ nsamples, minbuffer, next_timestamp,p->timestamp,p->timestamp+p->nsamples);
+ }
+#endif
/* Stop playing for a bit until the buffer re-fills */
pthread_mutex_unlock(&lock);
backend->deactivate();
/* Stop playing for a bit until the buffer re-fills */
pthread_mutex_unlock(&lock);
backend->deactivate();