From 8e3fe3d8ea9f5e75b6f1be4e4ae8cf2c16db88e3 Mon Sep 17 00:00:00 2001 Message-Id: <8e3fe3d8ea9f5e75b6f1be4e4ae8cf2c16db88e3.1714039426.git.mdw@distorted.org.uk> From: Mark Wooding Date: Fri, 5 Oct 2007 13:31:22 +0100 Subject: [PATCH] start on playrtp split Organization: Straylight/Edgeware From: rjk@greenend.org.uk <> --- clients/Makefile.am | 2 +- clients/playrtp-mem.c | 102 ++++++++++++++++++++++++ clients/playrtp.c | 175 ++++-------------------------------------- clients/playrtp.h | 142 ++++++++++++++++++++++++++++++++++ lib/heap.h | 89 +++++++++++---------- lib/test.c | 1 + 6 files changed, 310 insertions(+), 201 deletions(-) create mode 100644 clients/playrtp-mem.c create mode 100644 clients/playrtp.h diff --git a/clients/Makefile.am b/clients/Makefile.am index e2f5ec9..7b6cd39 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -34,7 +34,7 @@ disorderfm_SOURCES=disorderfm.c \ disorderfm_LDADD=$(LIBOBJS) ../lib/libdisorder.a $(LIBGC) $(LIBICONV) disorderfm_DEPENDENCIES=$(LIBOBJS) ../lib/libdisorder.a -disorder_playrtp_SOURCES=playrtp.c +disorder_playrtp_SOURCES=playrtp.c playrtp.h playrtp-mem.c disorder_playrtp_LDADD=$(LIBOBJS) ../lib/libdisorder.a \ $(LIBASOUND) $(COREAUDIO) disorder_playrtp_DEPENDENCIES=$(LIBOBJS) ../lib/libdisorder.a diff --git a/clients/playrtp-mem.c b/clients/playrtp-mem.c new file mode 100644 index 0000000..e101978 --- /dev/null +++ b/clients/playrtp-mem.c @@ -0,0 +1,102 @@ +/* + * This file is part of DisOrder. + * Copyright (C) 2007 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +/** @file clients/playrtp-mem.c + * @brief RTP player memory management + */ + +#include +#include "types.h" + +#include +#include +#include + +#include "mem.h" +#include "vector.h" +#include "heap.h" +#include "playrtp.h" + +/** @brief Linked list of free packets + * + * This is a linked list of formerly used packets. For preference we re-use + * packets that have already been used rather than unused ones, to limit the + * size of the program's working set. If there are no free packets in the list + * we try @ref next_free_packet instead. + * + * Must hold @ref lock when accessing this. + */ +static union free_packet *free_packets; + +/** @brief Array of new free packets + * + * There are @ref count_free_packets ready to use at this address. If there + * are none left we allocate more memory. + * + * Must hold @ref lock when accessing this. + */ +static union free_packet *next_free_packet; + +/** @brief Count of new free packets at @ref next_free_packet + * + * Must hold @ref lock when accessing this. + */ +static size_t count_free_packets; + +/** @brief Lock protecting packet allocator */ +static pthread_mutex_t mem_lock = PTHREAD_MUTEX_INITIALIZER; + +/** @brief Return a new packet */ +struct packet *new_packet(void) { + struct packet *p; + + pthread_mutex_lock(&mem_lock); + if(free_packets) { + p = &free_packets->p; + free_packets = free_packets->next; + } else { + if(!count_free_packets) { + next_free_packet = xcalloc(1024, sizeof (union free_packet)); + count_free_packets = 1024; + } + p = &(next_free_packet++)->p; + --count_free_packets; + } + pthread_mutex_unlock(&mem_lock); + return p; +} + +/** @brief Free a packet */ +void free_packet(struct packet *p) { + union free_packet *u = (union free_packet *)p; + pthread_mutex_lock(&mem_lock); + u->next = free_packets; + free_packets = u; + pthread_mutex_unlock(&mem_lock); +} + + +/* +Local Variables: +c-basic-offset:2 +comment-column:40 +fill-column:79 +indent-tabs-mode:nil +End: +*/ diff --git a/clients/playrtp.c b/clients/playrtp.c index 8931b6e..86a248c 100644 --- a/clients/playrtp.c +++ b/clients/playrtp.c @@ -36,7 +36,7 @@ * means it waits until ALSA says it's ready for more audio which it then * plays. * - * InCore Audio the main thread is only responsible for starting and stopping + * 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. * @@ -62,6 +62,7 @@ #include #include #include +#include #include "log.h" #include "mem.h" @@ -73,6 +74,7 @@ #include "vector.h" #include "heap.h" #include "timeval.h" +#include "playrtp.h" #if HAVE_COREAUDIO_AUDIOHARDWARE_H # include @@ -90,13 +92,7 @@ static int rtpfd; static FILE *logfp; /** @brief Output device */ -static const char *device; - -/** @brief Maximum samples per packet we'll support - * - * NB that two channels = two samples in this program. - */ -#define MAXSAMPLES 2048 +const char *device; /** @brief Minimum low watermark * @@ -113,79 +109,6 @@ static unsigned readahead = 2 * 2 * 44100; * We'll stop reading from the network if we have this many samples. */ static unsigned maxbuffer; -/** @brief Number of samples to infill by in one go - * - * This is an upper bound - in practice we expect the underlying audio API to - * only ask for a much smaller number of samples in any one go. - */ -#define INFILL_SAMPLES (44100 * 2) /* 1s */ - -/** @brief Received packet - * - * Received packets are kept in a binary heap (see @ref pheap) ordered by - * timestamp. - */ -struct packet { - /** @brief Next packet in @ref next_free_packet or @ref received_packets */ - struct packet *next; - - /** @brief Number of samples in this packet */ - uint32_t nsamples; - - /** @brief Timestamp from RTP packet - * - * NB that "timestamps" are really sample counters. Use lt() or lt_packet() - * to compare timestamps. - */ - uint32_t timestamp; - - /** @brief Flags - * - * Valid values are: - * - @ref IDLE - the idle bit was set in the RTP packet - */ - unsigned flags; -/** @brief idle bit set in RTP packet*/ -#define IDLE 0x0001 - - /** @brief Raw sample data - * - * Only the first @p nsamples samples are defined; the rest is uninitialized - * data. - */ - uint16_t samples_raw[MAXSAMPLES]; -}; - -/** @brief Return true iff \f$a < b\f$ in sequence-space arithmetic - * - * Specifically it returns true if \f$(a-b) mod 2^{32} < 2^{31}\f$. - * - * See also lt_packet(). - */ -static inline int lt(uint32_t a, uint32_t b) { - return (uint32_t)(a - b) & 0x80000000; -} - -/** @brief Return true iff a >= b in sequence-space arithmetic */ -static inline int ge(uint32_t a, uint32_t b) { - return !lt(a, b); -} - -/** @brief Return true iff a > b in sequence-space arithmetic */ -static inline int gt(uint32_t a, uint32_t b) { - return lt(b, a); -} - -/** @brief Return true iff a <= b in sequence-space arithmetic */ -static inline int le(uint32_t a, uint32_t b) { - return !lt(b, a); -} - -/** @brief Ordering for packets, used by @ref pheap */ -static inline int lt_packet(const struct packet *a, const struct packet *b) { - return lt(a->timestamp, b->timestamp); -} - /** @brief Received packets * Protected by @ref receive_lock * @@ -193,94 +116,57 @@ static inline int lt_packet(const struct packet *a, const struct packet *b) { * it and adds them to @ref packets. Whenever a packet is added to it, @ref * receive_cond is signalled. */ -static struct packet *received_packets; +struct packet *received_packets; /** @brief Tail of @ref received_packets * Protected by @ref receive_lock */ -static struct packet **received_tail = &received_packets; +struct packet **received_tail = &received_packets; /** @brief Lock protecting @ref received_packets * * Only listen_thread() and queue_thread() ever hold this lock. It is vital * that queue_thread() not hold it any longer than it strictly has to. */ -static pthread_mutex_t receive_lock = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t receive_lock = PTHREAD_MUTEX_INITIALIZER; /** @brief Condition variable signalled when @ref received_packets is updated * * Used by listen_thread() to notify queue_thread() that it has added another * packet to @ref received_packets. */ -static pthread_cond_t receive_cond = PTHREAD_COND_INITIALIZER; +pthread_cond_t receive_cond = PTHREAD_COND_INITIALIZER; /** @brief Length of @ref received_packets */ -static uint32_t nreceived; - -/** @struct pheap - * @brief Binary heap of packets ordered by timestamp */ -HEAP_TYPE(pheap, struct packet *, lt_packet); +uint32_t nreceived; /** @brief Binary heap of received packets */ -static struct pheap packets; +struct pheap packets; /** @brief Total number of samples available * * We make this volatile because we inspect it without a protecting lock, * so the usual pthread_* guarantees aren't available. */ -static volatile uint32_t nsamples; +volatile uint32_t nsamples; /** @brief Timestamp of next packet to play. * * This is set to the timestamp of the last packet, plus the number of * samples it contained. Only valid if @ref active is nonzero. */ -static uint32_t next_timestamp; +uint32_t next_timestamp; /** @brief True if actively playing * * This is true when playing and false when just buffering. */ -static int active; +int active; /** @brief Lock protecting @ref packets */ -static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; /** @brief Condition variable signalled whenever @ref packets is changed */ -static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; - -/** @brief Structure of free packet list */ -union free_packet { - struct packet p; - union free_packet *next; -}; - -/** @brief Linked list of free packets - * - * This is a linked list of formerly used packets. For preference we re-use - * packets that have already been used rather than unused ones, to limit the - * size of the program's working set. If there are no free packets in the list - * we try @ref next_free_packet instead. - * - * Must hold @ref lock when accessing this. - */ -static union free_packet *free_packets; - -/** @brief Array of new free packets - * - * There are @ref count_free_packets ready to use at this address. If there - * are none left we allocate more memory. - * - * Must hold @ref lock when accessing this. - */ -static union free_packet *next_free_packet; +pthread_cond_t cond = PTHREAD_COND_INITIALIZER; -/** @brief Count of new free packets at @ref next_free_packet - * - * Must hold @ref lock when accessing this. - */ -static size_t count_free_packets; - -/** @brief Lock protecting packet allocator */ -static pthread_mutex_t mem_lock = PTHREAD_MUTEX_INITIALIZER; +HEAP_DEFINE(pheap, struct packet *, lt_packet); static const struct option options[] = { { "help", no_argument, 0, 'h' }, @@ -295,35 +181,6 @@ static const struct option options[] = { { 0, 0, 0, 0 } }; -/** @brief Return a new packet */ -static struct packet *new_packet(void) { - struct packet *p; - - pthread_mutex_lock(&mem_lock); - if(free_packets) { - p = &free_packets->p; - free_packets = free_packets->next; - } else { - if(!count_free_packets) { - next_free_packet = xcalloc(1024, sizeof (union free_packet)); - count_free_packets = 1024; - } - p = &(next_free_packet++)->p; - --count_free_packets; - } - pthread_mutex_unlock(&mem_lock); - return p; -} - -/** @brief Free a packet */ -static void free_packet(struct packet *p) { - union free_packet *u = (union free_packet *)p; - pthread_mutex_lock(&mem_lock); - u->next = free_packets; - free_packets = u; - pthread_mutex_unlock(&mem_lock); -} - /** @brief Drop the first packet * * Assumes that @ref lock is held. diff --git a/clients/playrtp.h b/clients/playrtp.h new file mode 100644 index 0000000..7885a28 --- /dev/null +++ b/clients/playrtp.h @@ -0,0 +1,142 @@ +/* + * This file is part of DisOrder. + * Copyright (C) 2007 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +/** @file clients/playrtp.h + * @brief RTP player + */ + +#ifndef PLAYRTP_H +#define PLAYRTP_H + +/** @brief Maximum samples per packet we'll support + * + * NB that two channels = two samples in this program. + */ +#define MAXSAMPLES 2048 + +/** @brief Number of samples to infill by in one go + * + * This is an upper bound - in practice we expect the underlying audio API to + * only ask for a much smaller number of samples in any one go. + */ +#define INFILL_SAMPLES (44100 * 2) /* 1s */ + +/** @brief Received packet + * + * Received packets are kept in a binary heap (see @ref pheap) ordered by + * timestamp. + */ +struct packet { + /** @brief Next packet in @ref next_free_packet or @ref received_packets */ + struct packet *next; + + /** @brief Number of samples in this packet */ + uint32_t nsamples; + + /** @brief Timestamp from RTP packet + * + * NB that "timestamps" are really sample counters. Use lt() or lt_packet() + * to compare timestamps. + */ + uint32_t timestamp; + + /** @brief Flags + * + * Valid values are: + * - @ref IDLE - the idle bit was set in the RTP packet + */ + unsigned flags; +/** @brief idle bit set in RTP packet*/ +#define IDLE 0x0001 + + /** @brief Raw sample data + * + * Only the first @p nsamples samples are defined; the rest is uninitialized + * data. + */ + uint16_t samples_raw[MAXSAMPLES]; +}; + +/** @brief Structure of free packet list */ +union free_packet { + struct packet p; + union free_packet *next; +}; + +/** @brief Return true iff \f$a < b\f$ in sequence-space arithmetic + * + * Specifically it returns true if \f$(a-b) mod 2^{32} < 2^{31}\f$. + * + * See also lt_packet(). + */ +static inline int lt(uint32_t a, uint32_t b) { + return (uint32_t)(a - b) & 0x80000000; +} + +/** @brief Return true iff a >= b in sequence-space arithmetic */ +static inline int ge(uint32_t a, uint32_t b) { + return !lt(a, b); +} + +/** @brief Return true iff a > b in sequence-space arithmetic */ +static inline int gt(uint32_t a, uint32_t b) { + return lt(b, a); +} + +/** @brief Return true iff a <= b in sequence-space arithmetic */ +static inline int le(uint32_t a, uint32_t b) { + return !lt(b, a); +} + +/** @brief Ordering for packets, used by @ref pheap */ +static inline int lt_packet(const struct packet *a, const struct packet *b) { + return lt(a->timestamp, b->timestamp); +} + +/** @struct pheap + * @brief Binary heap of packets ordered by timestamp */ +HEAP_TYPE(pheap, struct packet *, lt_packet); + +struct packet *new_packet(void); +void free_packet(struct packet *p); + +extern const char *device; +extern struct packet *received_packets; +extern struct packet **received_tail; +extern pthread_mutex_t receive_lock; +extern pthread_cond_t receive_cond; +extern uint32_t nreceived; +extern struct pheap packets; +extern volatile uint32_t nsamples; +extern uint32_t next_timestamp; +extern int active; +extern pthread_mutex_t lock; +extern pthread_cond_t cond; + + +#endif /* PLAYRTP_H */ + +/* +Local Variables: +c-basic-offset:2 +comment-column:40 +fill-column:79 +indent-tabs-mode:nil +End: +*/ diff --git a/lib/heap.h b/lib/heap.h index 56821bf..ebd2f60 100644 --- a/lib/heap.h +++ b/lib/heap.h @@ -89,49 +89,56 @@ return heap->vec[0]; \ } \ \ - static void NAME##_insert(struct NAME *heap, NAME##_element elt) { \ - int n = heap->nvec; \ - NAME##_append(heap, elt); \ - while(n > 0) { \ - const int p = (n-1)/2; \ - if(!LT(heap->vec[n],heap->vec[p])) \ - break; \ - else { \ - const NAME##_element t = heap->vec[n]; \ - heap->vec[n] = heap->vec[p]; \ - heap->vec[p] = t; \ - n = p; \ - } \ - } \ - } \ - \ - static NAME##_element NAME##_remove(struct NAME *heap) { \ - int n = 0; \ - NAME##_element r; \ - \ - assert(heap->nvec > 0); \ - r = heap->vec[0]; \ - heap->vec[0] = heap->vec[--heap->nvec]; \ - while(2 * n + 1 < heap->nvec) { \ - int a = 2 * n + 1; \ - int b = 2 * n + 2; \ - \ - if(b < heap->nvec && LT(heap->vec[b],heap->vec[a])) { \ - ++a; \ - --b; \ - } \ - if(LT(heap->vec[a], heap->vec[n])) { \ - const NAME##_element t = heap->vec[n]; \ - heap->vec[n] = heap->vec[a]; \ - heap->vec[a] = t; \ - n = a; \ - } else \ - break; \ - } \ - return r; \ - } \ + void NAME##_insert(struct NAME *heap, NAME##_element elt); \ + NAME##_element NAME##_remove(struct NAME *heap); \ \ struct heap_swallow_semicolon + +/** @brief External-linkage definitions for @ref HEAP_TYPE */ +#define HEAP_DEFINE(NAME, ETYPE, LT) \ + void NAME##_insert(struct NAME *heap, NAME##_element elt) { \ + int n = heap->nvec; \ + NAME##_append(heap, elt); \ + while(n > 0) { \ + const int p = (n-1)/2; \ + if(!LT(heap->vec[n],heap->vec[p])) \ + break; \ + else { \ + const NAME##_element t = heap->vec[n]; \ + heap->vec[n] = heap->vec[p]; \ + heap->vec[p] = t; \ + n = p; \ + } \ + } \ + } \ + \ + NAME##_element NAME##_remove(struct NAME *heap) { \ + int n = 0; \ + NAME##_element r; \ + \ + assert(heap->nvec > 0); \ + r = heap->vec[0]; \ + heap->vec[0] = heap->vec[--heap->nvec]; \ + while(2 * n + 1 < heap->nvec) { \ + int a = 2 * n + 1; \ + int b = 2 * n + 2; \ + \ + if(b < heap->nvec && LT(heap->vec[b],heap->vec[a])) { \ + ++a; \ + --b; \ + } \ + if(LT(heap->vec[a], heap->vec[n])) { \ + const NAME##_element t = heap->vec[n]; \ + heap->vec[n] = heap->vec[a]; \ + heap->vec[a] = t; \ + n = a; \ + } else \ + break; \ + } \ + return r; \ + } \ + \ + struct heap_swallow_semicolon \ #endif /* PQUEUE_H */ diff --git a/lib/test.c b/lib/test.c index ed90183..b2e1dc5 100644 --- a/lib/test.c +++ b/lib/test.c @@ -380,6 +380,7 @@ static inline int int_lt(int a, int b) { return a < b; } /** @struct iheap * @brief A heap with @c int elements */ HEAP_TYPE(iheap, int, int_lt); +HEAP_DEFINE(iheap, int, int_lt); /** @brief Tests for @ref heap.h */ static void test_heap(void) { -- [mdw]