[pulseaudio-commits] 53 commits - bootstrap.sh configure.ac man/pulse-daemon.conf.5.xml.in shell-completion/zsh src/daemon src/Makefile.am src/modules src/pulsecore src/tests src/utils vala/libpulse.vapi

Tanu Kaskinen tanuk at kemper.freedesktop.org
Thu Jan 19 01:31:05 UTC 2017


 bootstrap.sh                                    |    7 
 configure.ac                                    |   36 
 man/pulse-daemon.conf.5.xml.in                  |    8 
 shell-completion/zsh/_pulseaudio                |   24 
 src/Makefile.am                                 |   10 
 src/daemon/daemon-conf.c                        |    4 
 src/daemon/daemon-conf.h                        |    1 
 src/daemon/daemon.conf.in                       |    1 
 src/daemon/main.c                               |    1 
 src/modules/alsa/alsa-util.c                    |    5 
 src/modules/bluetooth/backend-native.c          |   28 
 src/modules/bluetooth/module-bluetooth-policy.c |   43 
 src/modules/raop/base64.c                       |  124 -
 src/modules/raop/base64.h                       |   32 
 src/modules/raop/module-raop-discover.c         |  258 ++-
 src/modules/raop/module-raop-sink.c             |  641 --------
 src/modules/raop/raop-client.c                  | 1768 ++++++++++++++++++++++++
 src/modules/raop/raop-client.h                  |   83 +
 src/modules/raop/raop-crypto.c                  |  164 ++
 src/modules/raop/raop-crypto.h                  |   35 
 src/modules/raop/raop-packet-buffer.c           |  161 ++
 src/modules/raop/raop-packet-buffer.h           |   40 
 src/modules/raop/raop-sink.c                    |  674 +++++++++
 src/modules/raop/raop-sink.h                    |   33 
 src/modules/raop/raop-util.c                    |  211 ++
 src/modules/raop/raop-util.h                    |   39 
 src/modules/raop/raop_client.c                  |  581 -------
 src/modules/raop/raop_client.h                  |   42 
 src/modules/rtp/rtsp_client.c                   |  173 +-
 src/modules/rtp/rtsp_client.h                   |   60 
 src/modules/x11/module-x11-bell.c               |    5 
 src/pulsecore/core-util.c                       |   42 
 src/pulsecore/core-util.h                       |    7 
 src/pulsecore/core.c                            |    1 
 src/pulsecore/core.h                            |    1 
 src/pulsecore/parseaddr.c                       |   11 
 src/pulsecore/parseaddr.h                       |    2 
 src/pulsecore/resampler.c                       |   82 -
 src/pulsecore/resampler.h                       |    3 
 src/pulsecore/sink-input.c                      |    2 
 src/pulsecore/socket-client.c                   |   21 
 src/pulsecore/source-output.c                   |    2 
 src/tests/alsa-time-test.c                      |    3 
 src/tests/remix-test.c                          |   39 
 src/utils/padsp.in                              |    4 
 vala/libpulse.vapi                              |  136 -
 46 files changed, 3950 insertions(+), 1698 deletions(-)

New commits:
commit b365d7e60c46feb140c82a11428b2ca38e784dfc
Author: ced2c <cedric.hottier at gmail.com>
Date:   Sun Nov 6 12:54:27 2016 -0600

    raop: Fix #37: OOB access in rtsp_auth_cb
    
    Allocation for Apple-Challenge key is now defined to 16 bytes
    (instead of 16 bits)
    
    This patch fixes Issue #37
    https://github.com/hfujita/pulseaudio-raop2/issues/37

diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
index d695ce9..94342d2 100644
--- a/src/modules/raop/raop-client.c
+++ b/src/modules/raop/raop-client.c
@@ -86,6 +86,7 @@
 #define VOLUME_MIN -144.0
 
 #define UDP_DEFAULT_PKT_BUF_SIZE 1000
+#define APPLE_CHALLENGE_LENGTH 16
 
 struct pa_raop_client {
     pa_core *core;
@@ -1190,7 +1191,7 @@ static void rtsp_auth_cb(pa_rtsp_client *rtsp, pa_rtsp_state_t state, pa_rtsp_st
     switch (state) {
         case STATE_CONNECT: {
             char *sci = NULL, *sac = NULL;
-            uint16_t rac;
+            uint8_t rac[APPLE_CHALLENGE_LENGTH];
             struct {
                 uint32_t ci1;
                 uint32_t ci2;
@@ -1201,9 +1202,9 @@ static void rtsp_auth_cb(pa_rtsp_client *rtsp, pa_rtsp_state_t state, pa_rtsp_st
             sci = pa_sprintf_malloc("%08x%08x",rci.ci1, rci.ci2);
             pa_rtsp_add_header(c->rtsp, "Client-Instance", sci);
 
-            pa_random(&rac, sizeof(rac));
+            pa_random(rac, APPLE_CHALLENGE_LENGTH);
             /* Generate a random Apple-Challenge key */
-            pa_raop_base64_encode(&rac, 8 * sizeof(rac), &sac);
+            pa_raop_base64_encode(rac, APPLE_CHALLENGE_LENGTH, &sac);
             rtrim_char(sac, '=');
             pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
 

commit 6aaf2b0a1f9ad70161098fa9211967627ff6ea3a
Author: ced2c <cedric.hottier at gmail.com>
Date:   Sun Nov 6 12:54:26 2016 -0600

    raop: Fix #36: invalid access to freed object
    
    The RTSP client is not waiting anymore a new header after the
    previous one (which can never occurs if RAOP is disconnected)
    but after sending a command.
    
    This patch fixes Issue #36.
    https://github.com/hfujita/pulseaudio-raop2/issues/36

diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index fd5d2d8..34210f9 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -236,7 +236,6 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
 
         pa_log_debug("Full response received. Dispatching");
         headers_read(c);
-        c->waiting = 1;
         goto exit;
     }
 
@@ -481,7 +480,8 @@ static int rtsp_exec(pa_rtsp_client *c, const char *cmd,
     pa_log_debug(hdrs);*/
     pa_ioline_puts(c->ioline, hdrs);
     pa_xfree(hdrs);
-
+    /* The command is sent we can configure the rtsp client structure to handle a new answer */
+    c->waiting = 1;
     return 0;
 }
 

commit ab7b7ee24946058aa8185d1b400d63619676baab
Author: Hajime Fujita <crisp.fujita at nifty.com>
Date:   Sun Nov 6 12:54:25 2016 -0600

    raop: Fix memory leaks
    
    This patch fixes several memory leaks, and thereby fixes Issue #35.
    (https://github.com/hfujita/pulseaudio-raop2/issues/35)

diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
index f71ad9d..d695ce9 100644
--- a/src/modules/raop/raop-client.c
+++ b/src/modules/raop/raop-client.c
@@ -537,6 +537,7 @@ static ssize_t resend_udp_audio_packets(pa_raop_client *c, uint16_t seq, uint16_
     return total;
 }
 
+/* Caller has to free the allocated memory region for packet */
 static size_t build_udp_sync_packet(pa_raop_client *c, uint32_t stamp, uint32_t **packet) {
     const size_t size = sizeof(udp_sync_header) + 12;
     const uint32_t delay = 88200;
@@ -570,8 +571,10 @@ static ssize_t send_udp_sync_packet(pa_raop_client *c, uint32_t stamp) {
     size_t size = 0;
 
     size = build_udp_sync_packet(c, stamp, &packet);
-    if (packet != NULL && size > 0)
+    if (packet != NULL && size > 0) {
         written = pa_loop_write(c->udp_cfd, packet, size, NULL);
+        pa_xfree(packet);
+    }
 
     return written;
 }
@@ -606,6 +609,7 @@ static size_t handle_udp_control_packet(pa_raop_client *c, const uint8_t packet[
     return written;
 }
 
+/* Caller has to free the allocated memory region for packet */
 static size_t build_udp_timing_packet(pa_raop_client *c, const uint32_t data[6], uint64_t received, uint32_t **packet) {
     const size_t size = sizeof(udp_timing_header) + 24;
     uint32_t *buffer = NULL;
@@ -638,8 +642,10 @@ static ssize_t send_udp_timing_packet(pa_raop_client *c, const uint32_t data[6],
     size_t size = 0;
 
     size = build_udp_timing_packet(c, data, received, &packet);
-    if (packet != NULL && size > 0)
+    if (packet != NULL && size > 0) {
         written = pa_loop_write(c->udp_tfd, packet, size, NULL);
+        pa_xfree(packet);
+    }
 
     return written;
 }
@@ -1366,7 +1372,7 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_prot
 
     c = pa_xnew0(pa_raop_client, 1);
     c->core = core;
-    c->host = pa_xstrdup(a.path_or_host);
+    c->host = a.path_or_host; /* Will eventually be freed on destruction of c */
     if (a.port > 0)
         c->port = a.port;
     else

commit d7721032ea3aee1a7845f7853d7978255c367296
Author: Hajime Fujita <crisp.fujita at nifty.com>
Date:   Sun Nov 6 12:54:24 2016 -0600

    raop: Discard data upon getting EAGAIN on a socket
    
    This patch discards audio data when a socket returns EAGAIN.
    This was made based on a suggestion by karlstav
    (https://github.com/karlstav), and is supposed to solve Issue #32.
    (https://github.com/hfujita/pulseaudio-raop2/issues/32)

diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
index 68d188a..f71ad9d 100644
--- a/src/modules/raop/raop-client.c
+++ b/src/modules/raop/raop-client.c
@@ -471,8 +471,7 @@ static ssize_t send_udp_audio_packet(pa_raop_client *c, pa_memchunk *block, size
         written = pa_write(c->udp_sfd, buffer, packet->length, NULL);
     if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
         pa_log_debug("Discarding UDP (audio, seq=%d) packet due to EAGAIN (%s)", c->seq, pa_cstrerror(errno));
-        pa_memblock_release(packet->memblock);
-        return (ssize_t) packet->length;
+        written = packet->length;
     }
 
     pa_memblock_release(packet->memblock);

commit 3e26f2d15e095edf39048e35eb6953f559268dd8
Author: Hajime Fujita <crisp.fujita at nifty.com>
Date:   Sun Nov 6 12:54:23 2016 -0600

    raop: Disable is_recording flag when tearing down the connection
    
    This patch is based on a similar idea as the previous one -- disabling
    the flag right after the session is getting closed, rather than waiting
    for a response from the server.

diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
index bbef59d..68d188a 100644
--- a/src/modules/raop/raop-client.c
+++ b/src/modules/raop/raop-client.c
@@ -1120,8 +1120,6 @@ static void rtsp_stream_cb(pa_rtsp_client *rtsp, pa_rtsp_state_t state, pa_rtsp_
         case STATE_TEARDOWN: {
             pa_log_debug("RAOP: TEARDOWN");
 
-            c->is_recording = false;
-
             if (c->tcp_sfd > 0)
                 pa_close(c->tcp_sfd);
             c->tcp_sfd = -1;
@@ -1631,6 +1629,8 @@ int pa_raop_client_teardown(pa_raop_client *c) {
         return 1;
     }
 
+    c->is_recording = false;
+
     rv = pa_rtsp_teardown(c->rtsp);
     return rv;
 }

commit f3aa588a61a5c23958752868d5fec855fa12b94f
Author: Hajime Fujita <crisp.fujita at nifty.com>
Date:   Sun Nov 6 12:54:22 2016 -0600

    raop: Stop recording when RTSP FLUSH is issued
    
    This patch fixes the issue #31.
    https://github.com/hfujita/pulseaudio-raop2/issues/31
    
    This patch sets c->is_recording = false when the RTSP FLUSH command
    is issued. This avoids a race between the server response and
    the record activation in some cases.

diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
index 52b2dee..bbef59d 100644
--- a/src/modules/raop/raop-client.c
+++ b/src/modules/raop/raop-client.c
@@ -1114,8 +1114,6 @@ static void rtsp_stream_cb(pa_rtsp_client *rtsp, pa_rtsp_state_t state, pa_rtsp_
         case STATE_FLUSH: {
             pa_log_debug("RAOP: FLUSHED");
 
-            c->is_recording = false;
-
             break;
         }
 
@@ -1614,6 +1612,8 @@ int pa_raop_client_flush(pa_raop_client *c) {
         return 1;
     }
 
+    c->is_recording = false;
+
     rv = pa_rtsp_flush(c->rtsp, c->seq, c->rtptime);
     return rv;
 }

commit cd09b886911c6d1e1775381c6a59cfd505d8fb05
Author: Martin Blanchard <tchaik at gmx.com>
Date:   Sun Nov 6 12:54:21 2016 -0600

    raop: Add back initial volume RTSP SET_PARAMETER request on connect
    
    Regression introduced in commit 8c6407f:
    raop: Merge TCP and UDP code paths + refactoring
    
    Anyway, we need to determine if initial volume has to be setup before
    sending RECORD or after:
    
    - Setting it up *before* shouldn't be a problem: sink.c waits for
    CONNECT state, set the volume and client.c triggers RECORD only once
    he's got the SET_PARAMETER reply from server.
    
    - Setting it up *after* seems to be more difficult if we try not to
    send any audio before receiving the SET_PARAMETER reply form server. A
    solution may be to send SET_PARAMETER just after the RECORD server
    response is received and hope that it get processed by server during the
    2sec latency/buffering time...
    
    Attached patch implement that last solution. Works for me, but I cannot
    guaranty it will with your hardware...

diff --git a/src/modules/raop/raop-sink.c b/src/modules/raop/raop-sink.c
index 12fa12a..7d8fe36 100644
--- a/src/modules/raop/raop-sink.c
+++ b/src/modules/raop/raop-sink.c
@@ -95,6 +95,8 @@ enum {
 
 static void userdata_free(struct userdata *u);
 
+static void sink_set_volume_cb(pa_sink *s);
+
 static void raop_state_cb(pa_raop_state_t state, void *userdata) {
     struct userdata *u = userdata;
 
@@ -228,6 +230,9 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
                         /* Our stream has been suspended so we just flush it... */
                         pa_rtpoll_set_timer_disabled(u->rtpoll);
                         pa_raop_client_flush(u->raop);
+                    } else {
+                        /* Set the initial volume */
+                        sink_set_volume_cb(u->sink);
                     }
 
                     return 0;

commit b81ceb6707b7c8c9b7ef52017962e764989ea578
Author: Hajime Fujita <crisp.fujita at nifty.com>
Date:   Sun Nov 6 12:54:20 2016 -0600

    raop: Silently drop out-of-history retransmission request

diff --git a/src/modules/raop/raop-packet-buffer.c b/src/modules/raop/raop-packet-buffer.c
index 05f9592..72fd729 100644
--- a/src/modules/raop/raop-packet-buffer.c
+++ b/src/modules/raop/raop-packet-buffer.c
@@ -142,13 +142,15 @@ pa_memchunk *pa_raop_packet_buffer_retrieve(pa_raop_packet_buffer *pb, uint16_t
         if (seq < pb->seq) {
             /* Regular case: pb->seq did not wrapped since seq. */
             delta = pb->seq - seq;
-            pa_assert(delta <= pb->count);
         } else {
             /* Tricky case: pb->seq wrapped since seq! */
             delta = pb->seq + (UINT16_MAX - seq);
-            pa_assert(delta <= pb->count);
         }
 
+        /* If the requested packet is too old, do nothing and return */
+        if (delta > pb->count)
+            return NULL;
+
         i = (pb->size + pb->pos - delta) % pb->size;
 
         if (delta < pb->size && pb->packets[i].memblock)

commit 516906aef987ee13fbab0394d078060f7118c6cc
Author: Martin Blanchard <tchaik at gmx.com>
Date:   Sun Nov 6 12:54:19 2016 -0600

    raop: Correctly wrap RTP packet sequence number

diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
index 574f10b..52b2dee 100644
--- a/src/modules/raop/raop-client.c
+++ b/src/modules/raop/raop-client.c
@@ -24,6 +24,7 @@
 #endif
 
 #include <stdlib.h>
+#include <stdint.h>
 #include <string.h>
 #include <errno.h>
 #include <unistd.h>
@@ -316,10 +317,10 @@ static size_t build_tcp_audio_packet(pa_raop_client *c, pa_memchunk *block, pa_m
     buffer += packet->index / sizeof(uint32_t);
     raw += block->index;
 
-    if (c->seq == 0xFFFF) {
-        pa_log_debug("wrapping sequence number");
-        c->seq = pa_raop_packet_buffer_wrap_seq(c->pbuf, c->seq);
-    } else
+    /* Wrap sequence number to 0 then UINT16_MAX is reached */
+    if (c->seq == UINT16_MAX)
+        c->seq = 0;
+    else
         c->seq++;
 
     memcpy(buffer, tcp_audio_header, sizeof(tcp_audio_header));
@@ -360,13 +361,13 @@ static ssize_t send_tcp_audio_packet(pa_raop_client *c, pa_memchunk *block, size
     ssize_t written = -1;
     size_t done = 0;
 
-    if (!(packet = pa_raop_packet_buffer_get(c->pbuf, c->seq, max)))
+    if (!(packet = pa_raop_packet_buffer_retrieve(c->pbuf, c->seq)))
         return -1;
 
     if (packet->length <= 0) {
         pa_assert(block->index == offset);
 
-        if (!(packet = pa_raop_packet_buffer_get(c->pbuf, c->seq + 1, max)))
+        if (!(packet = pa_raop_packet_buffer_prepare(c->pbuf, c->seq + 1, max)))
             return -1;
 
         packet->index = 0;
@@ -427,10 +428,10 @@ static size_t build_udp_audio_packet(pa_raop_client *c, pa_memchunk *block, pa_m
 
     c->rtptime += length / 4;
 
-    if (c->seq == 0xFFFF) {
-        pa_log_debug("wrapping sequence number");
-        c->seq = pa_raop_packet_buffer_wrap_seq(c->pbuf, c->seq);
-    } else
+    /* Wrap sequence number to 0 then UINT16_MAX is reached */
+    if (c->seq == UINT16_MAX)
+        c->seq = 0;
+    else
         c->seq++;
 
     pa_memblock_release(block->memblock);
@@ -453,11 +454,11 @@ static ssize_t send_udp_audio_packet(pa_raop_client *c, pa_memchunk *block, size
     /* UDP packet has to be sent at once ! */
     pa_assert(block->index == offset);
 
-    if (!(packet = pa_raop_packet_buffer_get(c->pbuf, c->seq, max)))
+    if (!(packet = pa_raop_packet_buffer_prepare(c->pbuf, c->seq, max)))
         return -1;
 
-    packet->length = max;
     packet->index = sizeof(udp_audio_retrans_header);
+    packet->length = max - sizeof(udp_audio_retrans_header);
     if (!build_udp_audio_packet(c, block, packet))
         return -1;
 
@@ -508,7 +509,7 @@ static ssize_t resend_udp_audio_packets(pa_raop_client *c, uint16_t seq, uint16_
         uint8_t *buffer = NULL;
         ssize_t written = -1;
 
-        if (!(packet = pa_raop_packet_buffer_get(c->pbuf, seq + i, 0)))
+        if (!(packet = pa_raop_packet_buffer_retrieve(c->pbuf, seq + i)))
             continue;
 
         if (packet->index > 0) {
diff --git a/src/modules/raop/raop-packet-buffer.c b/src/modules/raop/raop-packet-buffer.c
index 4c45d18..05f9592 100644
--- a/src/modules/raop/raop-packet-buffer.c
+++ b/src/modules/raop/raop-packet-buffer.c
@@ -25,6 +25,7 @@
 #endif
 
 #include <stdlib.h>
+#include <stdint.h>
 #include <limits.h>
 
 #include <pulse/xmalloc.h>
@@ -37,7 +38,9 @@
 struct pa_raop_packet_buffer {
     pa_memchunk *packets;
     pa_mempool *mempool;
+
     size_t size;
+    size_t count;
 
     uint16_t seq;
     size_t pos;
@@ -49,6 +52,7 @@ pa_raop_packet_buffer *pa_raop_packet_buffer_new(pa_mempool *mempool, const size
     pa_assert(mempool);
     pa_assert(size > 0);
 
+    pb->count = 0;
     pb->size = size;
     pb->mempool = mempool;
     pb->packets = pa_xnew0(pa_memchunk, size);
@@ -80,7 +84,8 @@ void pa_raop_packet_buffer_reset(pa_raop_packet_buffer *pb, uint16_t seq) {
     pa_assert(pb->packets);
 
     pb->pos = 0;
-    pb->seq = seq - 1;
+    pb->count = 0;
+    pb->seq = (!seq) ? UINT16_MAX : seq - 1;
     for (i = 0; i < pb->size; i++) {
         if (pb->packets[i].memblock)
             pa_memblock_unref(pb->packets[i].memblock);
@@ -88,23 +93,43 @@ void pa_raop_packet_buffer_reset(pa_raop_packet_buffer *pb, uint16_t seq) {
     }
 }
 
-uint16_t pa_raop_packet_buffer_wrap_seq(pa_raop_packet_buffer *pb, uint16_t seq) {
-    int seq_shift;
+pa_memchunk *pa_raop_packet_buffer_prepare(pa_raop_packet_buffer *pb, uint16_t seq, const size_t size) {
+    pa_memchunk *packet = NULL;
+    size_t i;
 
     pa_assert(pb);
+    pa_assert(pb->packets);
+
+    if (seq == 0) {
+        /* 0 means seq reached UINT16_MAX and has been wrapped... */
+        pa_assert(pb->seq == UINT16_MAX);
+        pb->seq = 0;
+    } else {
+        /* ...otherwise, seq MUST have be increased! */
+        pa_assert(seq == pb->seq + 1);
+        pb->seq++;
+    }
+
+    i = (pb->pos + 1) % pb->size;
 
-    if (seq > pb->seq)
-        seq_shift = pb->seq - 1;
-    else
-        seq_shift = seq;
+    if (pb->packets[i].memblock)
+        pa_memblock_unref(pb->packets[i].memblock);
+    pa_memchunk_reset(&pb->packets[i]);
 
-    pb->seq -= seq_shift;
+    pb->packets[i].memblock = pa_memblock_new(pb->mempool, size);
+    pb->packets[i].length = size;
+    pb->packets[i].index = 0;
 
-    return seq - seq_shift;
+    packet = &pb->packets[i];
 
+    if (pb->count < pb->size)
+        pb->count++;
+    pb->pos = i;
+
+    return packet;
 }
 
-pa_memchunk *pa_raop_packet_buffer_get(pa_raop_packet_buffer *pb, uint16_t seq, const size_t size) {
+pa_memchunk *pa_raop_packet_buffer_retrieve(pa_raop_packet_buffer *pb, uint16_t seq) {
     pa_memchunk *packet = NULL;
     size_t delta, i;
 
@@ -113,20 +138,21 @@ pa_memchunk *pa_raop_packet_buffer_get(pa_raop_packet_buffer *pb, uint16_t seq,
 
     if (seq == pb->seq)
         packet = &pb->packets[pb->pos];
-    else if (seq < pb->seq) {
-        delta = pb->seq - seq;
+    else {
+        if (seq < pb->seq) {
+            /* Regular case: pb->seq did not wrapped since seq. */
+            delta = pb->seq - seq;
+            pa_assert(delta <= pb->count);
+        } else {
+            /* Tricky case: pb->seq wrapped since seq! */
+            delta = pb->seq + (UINT16_MAX - seq);
+            pa_assert(delta <= pb->count);
+        }
+
         i = (pb->size + pb->pos - delta) % pb->size;
-        if (delta < pb->size)
+
+        if (delta < pb->size && pb->packets[i].memblock)
             packet = &pb->packets[i];
-    } else {
-        i = (pb->pos + (seq - pb->seq)) % pb->size;
-        if (pb->packets[i].memblock)
-            pa_memblock_unref(pb->packets[i].memblock);
-        pa_memchunk_reset(&pb->packets[i]);
-        pb->packets[i].memblock = pa_memblock_new(pb->mempool, size);
-        packet = &pb->packets[i];
-        pb->seq = seq;
-        pb->pos = i;
     }
 
     return packet;
diff --git a/src/modules/raop/raop-packet-buffer.h b/src/modules/raop/raop-packet-buffer.h
index cb2bfdf..c410298 100644
--- a/src/modules/raop/raop-packet-buffer.h
+++ b/src/modules/raop/raop-packet-buffer.h
@@ -33,7 +33,8 @@ pa_raop_packet_buffer *pa_raop_packet_buffer_new(pa_mempool *mempool, const size
 void pa_raop_packet_buffer_free(pa_raop_packet_buffer *pb);
 
 void pa_raop_packet_buffer_reset(pa_raop_packet_buffer *pb, uint16_t seq);
-pa_memchunk *pa_raop_packet_buffer_get(pa_raop_packet_buffer *pb, uint16_t seq, const size_t size);
 
-uint16_t pa_raop_packet_buffer_wrap_seq(pa_raop_packet_buffer *pb, uint16_t seq);
+pa_memchunk *pa_raop_packet_buffer_prepare(pa_raop_packet_buffer *pb, uint16_t seq, const size_t size);
+pa_memchunk *pa_raop_packet_buffer_retrieve(pa_raop_packet_buffer *pb, uint16_t seq);
+
 #endif

commit 3de65e61e4ed682da0c8dccb7dd9eda4fef2b94f
Author: Hajime Fujita <crisp.fujita at nifty.com>
Date:   Sun Nov 6 12:54:18 2016 -0600

    raop: Add IPv6 support

diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
index cf415d0..574f10b 100644
--- a/src/modules/raop/raop-client.c
+++ b/src/modules/raop/raop-client.c
@@ -752,6 +752,7 @@ static int open_bind_udp_socket(pa_raop_client *c, uint16_t *actual_port) {
     if (inet_pton(AF_INET, pa_rtsp_localip(c->rtsp), &sa4.sin_addr) > 0) {
         sa4.sin_family = af = AF_INET;
         sa4.sin_port = htons(port);
+        sa4.sin_addr.s_addr = INADDR_ANY;
         sa = (struct sockaddr *) &sa4;
         salen = sizeof(sa4);
         sa_port = &sa4.sin_port;
@@ -759,6 +760,7 @@ static int open_bind_udp_socket(pa_raop_client *c, uint16_t *actual_port) {
     } else if (inet_pton(AF_INET6, pa_rtsp_localip(c->rtsp), &sa6.sin6_addr) > 0) {
         sa6.sin6_family = af = AF_INET6;
         sa6.sin6_port = htons(port);
+        sa6.sin6_addr = in6addr_any;
         sa = (struct sockaddr *) &sa6;
         salen = sizeof(sa6);
         sa_port = &sa6.sin6_port;
@@ -849,11 +851,18 @@ static void rtsp_stream_cb(pa_rtsp_client *rtsp, pa_rtsp_state_t state, pa_rtsp_
             int frames = 0;
             const char *ip;
             char *url;
+            int ipv;
 
             pa_log_debug("RAOP: CONNECTED");
 
             ip = pa_rtsp_localip(c->rtsp);
-            url = pa_sprintf_malloc("rtsp://%s/%s", ip, c->sid);
+            if (pa_is_ip6_address(ip)) {
+                ipv = 6;
+                url = pa_sprintf_malloc("rtsp://[%s]/%s", ip, c->sid);
+            } else {
+                ipv = 4;
+                url = pa_sprintf_malloc("rtsp://%s/%s", ip, c->sid);
+            }
             pa_rtsp_set_url(c->rtsp, url);
 
             if (c->protocol == PA_RAOP_PROTOCOL_TCP)
@@ -865,14 +874,14 @@ static void rtsp_stream_cb(pa_rtsp_client *rtsp, pa_rtsp_state_t state, pa_rtsp_
                 case PA_RAOP_ENCRYPTION_NONE: {
                     sdp = pa_sprintf_malloc(
                         "v=0\r\n"
-                        "o=iTunes %s 0 IN IP4 %s\r\n"
+                        "o=iTunes %s 0 IN IP%d %s\r\n"
                         "s=iTunes\r\n"
-                        "c=IN IP4 %s\r\n"
+                        "c=IN IP%d %s\r\n"
                         "t=0 0\r\n"
                         "m=audio 0 RTP/AVP 96\r\n"
                         "a=rtpmap:96 AppleLossless\r\n"
                         "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 44100\r\n",
-                        c->sid, ip, c->host, frames);
+                        c->sid, ipv, ip, ipv, c->host, frames);
 
                     break;
                 }
@@ -886,16 +895,16 @@ static void rtsp_stream_cb(pa_rtsp_client *rtsp, pa_rtsp_state_t state, pa_rtsp_
 
                     sdp = pa_sprintf_malloc(
                         "v=0\r\n"
-                        "o=iTunes %s 0 IN IP4 %s\r\n"
+                        "o=iTunes %s 0 IN IP%d %s\r\n"
                         "s=iTunes\r\n"
-                        "c=IN IP4 %s\r\n"
+                        "c=IN IP%d %s\r\n"
                         "t=0 0\r\n"
                         "m=audio 0 RTP/AVP 96\r\n"
                         "a=rtpmap:96 AppleLossless\r\n"
                         "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 44100\r\n"
                         "a=rsaaeskey:%s\r\n"
                         "a=aesiv:%s\r\n",
-                        c->sid, ip, c->host, frames, key, iv);
+                        c->sid, ipv, ip, ipv, c->host, frames, key, iv);
 
                     pa_xfree(key);
                     pa_xfree(iv);
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index 31b8673..fd5d2d8 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -330,7 +330,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
             }
         } else if (AF_INET6 == sa.sa.sa_family) {
             if ((res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf)))) {
-                c->localip = pa_sprintf_malloc("[%s]", res);
+                c->localip = pa_xstrdup(res);
             }
         }
     }

commit 3e53f47c8de04f598ab95cc16a467c1778684fc9
Author: Hajime Fujita <crisp.fujita at nifty.com>
Date:   Sun Nov 6 12:54:17 2016 -0600

    raop: Add address to RAOP device description
    
    Some time one device announces multiple addresses (e.g. IPv4 one
    and IPv6 one). Or some user may own multiple RAOP devices with
    the same model name.
    This patch adds device port to device description so that users
    can distinguish appropriate RAOP sink by its address.

diff --git a/src/modules/raop/module-raop-discover.c b/src/modules/raop/module-raop-discover.c
index bf05dec..c0b4549 100644
--- a/src/modules/raop/module-raop-discover.c
+++ b/src/modules/raop/module-raop-discover.c
@@ -241,19 +241,22 @@ static void resolver_cb(
     avahi_free(device);
     pa_xfree(dname);
 
+    avahi_address_snprint(at, sizeof(at), a);
     if (nicename) {
         args = pa_sprintf_malloc("server=[%s]:%u "
                                  "sink_name=%s "
-                                 "sink_properties='device.description=\"%s\"'",
-                                 avahi_address_snprint(at, sizeof(at), a), port,
+                                 "sink_properties='device.description=\"%s (%s:%u)\"'",
+                                 at, port,
                                  vname,
-                                 nicename);
+                                 nicename, at, port);
         pa_xfree(nicename);
     } else {
         args = pa_sprintf_malloc("server=[%s]:%u "
-                                 "sink_name=%s",
-                                 avahi_address_snprint(at, sizeof(at), a), port,
-                                 vname);
+                                 "sink_name=%s"
+                                 "sink_properties='device.description=\"%s:%u\"'",
+                                 at, port,
+                                 vname,
+                                 at, port);
     }
 
     if (tp != NULL) {

commit 751d88717e972410bbb5dd3f90033edbd89ffea5
Author: Stephen Paul Weber <github at singpolyma.net>
Date:   Sun Nov 6 12:54:16 2016 -0600

    raop: Do not flush when RTSP object is not ready
    
    This patch fixes a crash issue reported at
    https://github.com/hfujita/pulseaudio-raop2/issues/9

diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
index 90488e4..cf415d0 100644
--- a/src/modules/raop/raop-client.c
+++ b/src/modules/raop/raop-client.c
@@ -1596,7 +1596,7 @@ int pa_raop_client_flush(pa_raop_client *c) {
 
     pa_assert(c);
 
-    if (!c->rtsp) {
+    if (!c->rtsp || !pa_rtsp_exec_ready(c->rtsp)) {
         pa_log_debug("Cannot FLUSH, connection not established yet...)");
         return 0;
     } else if (!c->sci) {

commit f4bd06bfa5abbbf9b2e1bd1388a211ffdd8218e2
Author: Colin Leroy <colin at colino.net>
Date:   Sun Nov 6 12:54:15 2016 -0600

    raop: Fix packet retransmission
    
    Fix UDP header decoding (sequence number and number of packets);
    Fix missing pa_memblock_release() causing assertions after retransmission.

diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
index 8a43a19..90488e4 100644
--- a/src/modules/raop/raop-client.c
+++ b/src/modules/raop/raop-client.c
@@ -470,6 +470,7 @@ static ssize_t send_udp_audio_packet(pa_raop_client *c, pa_memchunk *block, size
         written = pa_write(c->udp_sfd, buffer, packet->length, NULL);
     if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
         pa_log_debug("Discarding UDP (audio, seq=%d) packet due to EAGAIN (%s)", c->seq, pa_cstrerror(errno));
+        pa_memblock_release(packet->memblock);
         return (ssize_t) packet->length;
     }
 
@@ -525,9 +526,11 @@ static ssize_t resend_udp_audio_packets(pa_raop_client *c, uint16_t seq, uint16_
             written = pa_write(c->udp_cfd, buffer, packet->length, NULL);
         if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
             pa_log_debug("Discarding UDP (audio-restransmitted, seq=%d) packet due to EAGAIN", seq + i);
+            pa_memblock_release(packet->memblock);
             continue;
         }
 
+        pa_memblock_release(packet->memblock);
         total +=  written;
     }
 
@@ -582,8 +585,8 @@ static size_t handle_udp_control_packet(pa_raop_client *c, const uint8_t packet[
     if (size != 8 || packet[0] != 0x80)
         return 1;
 
-    seq = ntohs((uint16_t) packet[4]);
-    nbp = ntohs((uint16_t) packet[6]);
+    seq = ntohs((uint16_t) (packet[4] | packet[5] << 8));
+    nbp = ntohs((uint16_t) (packet[6] | packet[7] << 8));
     if (nbp <= 0)
         return 1;
 

commit 4d163697071ed24a97d1b3e96aae27b84f69104a
Author: Colin Leroy <colin at colino.net>
Date:   Sun Nov 6 12:54:14 2016 -0600

    raop: fix typos

diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
index 8dc05a4..8a43a19 100644
--- a/src/modules/raop/raop-client.c
+++ b/src/modules/raop/raop-client.c
@@ -174,14 +174,14 @@ static const uint8_t udp_sync_header[8] = {
     0x00, 0x00, 0x00, 0x00
 };
 
-/* Timming packet header [8x8] (cf. rfc3550):
+/* Timing packet header [8x8] (cf. rfc3550):
  *  [0]   RTP v2: 0x80
  *  [1]   Payload type: 0x53 | Marker bit: 0x80 (always set)
  *  [2,3] Sequence number: 0x0007
  *  [4,7] Timestamp: 0x00000000 (unused) */
 #define PAYLOAD_TIMING_REQUEST  0x52
 #define PAYLOAD_TIMING_REPLY    0x53
-static const uint8_t udp_timming_header[8] = {
+static const uint8_t udp_timing_header[8] = {
     0x80, 0xd3, 0x00, 0x07,
     0x00, 0x00, 0x00, 0x00
 };
@@ -587,7 +587,7 @@ static size_t handle_udp_control_packet(pa_raop_client *c, const uint8_t packet[
     if (nbp <= 0)
         return 1;
 
-    /* The market bit is always set (see rfc3550 for packet structure) ! */
+    /* The marker bit is always set (see rfc3550 for packet structure) ! */
     payload = packet[1] ^ 0x80;
     switch (payload) {
         case PAYLOAD_RETRANSMIT_REQUEST:
@@ -604,7 +604,7 @@ static size_t handle_udp_control_packet(pa_raop_client *c, const uint8_t packet[
 }
 
 static size_t build_udp_timing_packet(pa_raop_client *c, const uint32_t data[6], uint64_t received, uint32_t **packet) {
-    const size_t size = sizeof(udp_timming_header) + 24;
+    const size_t size = sizeof(udp_timing_header) + 24;
     uint32_t *buffer = NULL;
     uint64_t transmitted = 0;
     struct timeval tv;
@@ -613,7 +613,7 @@ static size_t build_udp_timing_packet(pa_raop_client *c, const uint32_t data[6],
     if (!(buffer = pa_xmalloc0(size)))
         return 0;
 
-    memcpy(buffer, udp_timming_header, sizeof(udp_timming_header));
+    memcpy(buffer, udp_timing_header, sizeof(udp_timing_header));
     /* Copying originate timestamp from the incoming request packet. */
     buffer[2] = data[4];
     buffer[3] = data[5];
@@ -653,9 +653,9 @@ static size_t handle_udp_timing_packet(pa_raop_client *c, const uint8_t packet[]
         return 0;
 
     rci = timeval_to_ntp(pa_rtclock_get(&tv));
-    data = (uint32_t *) (packet + sizeof(udp_timming_header));
+    data = (uint32_t *) (packet + sizeof(udp_timing_header));
 
-    /* The market bit is always set (see rfc3550 for packet structure) ! */
+    /* The marker bit is always set (see rfc3550 for packet structure) ! */
     payload = packet[1] ^ 0x80;
     switch (payload) {
         case PAYLOAD_TIMING_REQUEST:

commit b95aebdb2d6aa003a5f99e2c6c0f30d611ecf398
Author: Colin Leroy <colin at colino.net>
Date:   Sun Nov 6 12:54:13 2016 -0600

    raop: fix sequence number overflow
    
    Wrap sequence number when we reach uint16_t's max 0xFFFF.

diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
index 81a68a0..8dc05a4 100644
--- a/src/modules/raop/raop-client.c
+++ b/src/modules/raop/raop-client.c
@@ -316,7 +316,12 @@ static size_t build_tcp_audio_packet(pa_raop_client *c, pa_memchunk *block, pa_m
     buffer += packet->index / sizeof(uint32_t);
     raw += block->index;
 
-    c->seq++;
+    if (c->seq == 0xFFFF) {
+        pa_log_debug("wrapping sequence number");
+        c->seq = pa_raop_packet_buffer_wrap_seq(c->pbuf, c->seq);
+    } else
+        c->seq++;
+
     memcpy(buffer, tcp_audio_header, sizeof(tcp_audio_header));
     buffer[1] |= htonl((uint32_t) c->seq);
     buffer[2] = htonl(c->rtptime);
@@ -421,7 +426,12 @@ static size_t build_udp_audio_packet(pa_raop_client *c, pa_memchunk *block, pa_m
     }
 
     c->rtptime += length / 4;
-    c->seq++;
+
+    if (c->seq == 0xFFFF) {
+        pa_log_debug("wrapping sequence number");
+        c->seq = pa_raop_packet_buffer_wrap_seq(c->pbuf, c->seq);
+    } else
+        c->seq++;
 
     pa_memblock_release(block->memblock);
 
diff --git a/src/modules/raop/raop-packet-buffer.c b/src/modules/raop/raop-packet-buffer.c
index 06e3125..4c45d18 100644
--- a/src/modules/raop/raop-packet-buffer.c
+++ b/src/modules/raop/raop-packet-buffer.c
@@ -88,13 +88,28 @@ void pa_raop_packet_buffer_reset(pa_raop_packet_buffer *pb, uint16_t seq) {
     }
 }
 
+uint16_t pa_raop_packet_buffer_wrap_seq(pa_raop_packet_buffer *pb, uint16_t seq) {
+    int seq_shift;
+
+    pa_assert(pb);
+
+    if (seq > pb->seq)
+        seq_shift = pb->seq - 1;
+    else
+        seq_shift = seq;
+
+    pb->seq -= seq_shift;
+
+    return seq - seq_shift;
+
+}
+
 pa_memchunk *pa_raop_packet_buffer_get(pa_raop_packet_buffer *pb, uint16_t seq, const size_t size) {
     pa_memchunk *packet = NULL;
     size_t delta, i;
 
     pa_assert(pb);
     pa_assert(pb->packets);
-    pa_assert(seq > 0);
 
     if (seq == pb->seq)
         packet = &pb->packets[pb->pos];
diff --git a/src/modules/raop/raop-packet-buffer.h b/src/modules/raop/raop-packet-buffer.h
index b8c7617..cb2bfdf 100644
--- a/src/modules/raop/raop-packet-buffer.h
+++ b/src/modules/raop/raop-packet-buffer.h
@@ -35,4 +35,5 @@ void pa_raop_packet_buffer_free(pa_raop_packet_buffer *pb);
 void pa_raop_packet_buffer_reset(pa_raop_packet_buffer *pb, uint16_t seq);
 pa_memchunk *pa_raop_packet_buffer_get(pa_raop_packet_buffer *pb, uint16_t seq, const size_t size);
 
+uint16_t pa_raop_packet_buffer_wrap_seq(pa_raop_packet_buffer *pb, uint16_t seq);
 #endif

commit 81fa11e3a925fdcf624db5e88d07e1fd442f9de0
Author: Colin Leroy <colin at colino.net>
Date:   Sun Nov 6 12:54:12 2016 -0600

    raop: Fix sink getting destroyed after one use
    
    It is expected to get disconnected after switching back to a
    different sink.

diff --git a/src/modules/raop/raop-sink.c b/src/modules/raop/raop-sink.c
index 54fda5f..12fa12a 100644
--- a/src/modules/raop/raop-sink.c
+++ b/src/modules/raop/raop-sink.c
@@ -252,7 +252,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
                     if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
                         pa_rtpoll_set_timer_disabled(u->rtpoll);
-                    else
+                    else if (u->sink->thread_info.state != PA_SINK_IDLE)
                         pa_module_unload_request(u->module, true);
 
                     return 0;

commit 1c56b869264c69b173b49961be9ae8c4971e85a2
Author: Martin Blanchard <tchaik at gmx.com>
Date:   Sun Nov 6 12:54:11 2016 -0600

    raop: Remove unimplemented code (PCM and AAC)

diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
index 195b00c..81a68a0 100644
--- a/src/modules/raop/raop-client.c
+++ b/src/modules/raop/raop-client.c
@@ -267,16 +267,6 @@ static inline void bit_writer(uint8_t **buffer, uint8_t *bit_pos, size_t *size,
     }
 }
 
-static size_t write_PCM_data(uint8_t *packet, const size_t max, uint8_t *raw, size_t *length) {
-    size_t size = 0;
-
-    pa_memzero(packet, max);
-
-    pa_log("Raw PCM not implemented...");
-
-    return size;
-}
-
 static size_t write_ALAC_data(uint8_t *packet, const size_t max, uint8_t *raw, size_t *length, bool compress) {
     uint32_t nbs = (*length / 2) / 2;
     uint8_t *ibp, *maxibp;
@@ -315,16 +305,6 @@ static size_t write_ALAC_data(uint8_t *packet, const size_t max, uint8_t *raw, s
     return size;
 }
 
-static size_t write_AAC_data(uint8_t *packet, const size_t max, uint8_t *raw, size_t *length) {
-    size_t size = 0;
-
-    pa_memzero(packet, max);
-
-    pa_log("AAC encoding not implemented...");
-
-    return size;
-}
-
 static size_t build_tcp_audio_packet(pa_raop_client *c, pa_memchunk *block, pa_memchunk *packet) {
     const size_t head = sizeof(tcp_audio_header);
     uint32_t *buffer = NULL;
@@ -344,12 +324,14 @@ static size_t build_tcp_audio_packet(pa_raop_client *c, pa_memchunk *block, pa_m
 
     length = block->length;
     size = sizeof(tcp_audio_header);
-    if (c->codec == PA_RAOP_CODEC_PCM)
-        size += write_PCM_data(((uint8_t *) buffer + head), packet->length - head, raw, &length);
-    else if (c->codec == PA_RAOP_CODEC_ALAC)
+    if (c->codec == PA_RAOP_CODEC_ALAC)
         size += write_ALAC_data(((uint8_t *) buffer + head), packet->length - head, raw, &length, false);
-    else
-        size += write_AAC_data(((uint8_t *) buffer + head), packet->length - head, raw, &length);
+    else {
+        pa_log_debug("Only ALAC encoding is supported, sending zeros...");
+        pa_memzero(((uint8_t *) buffer + head), packet->length - head);
+        size += length;
+    }
+
     c->rtptime += length / 4;
 
     pa_memblock_release(block->memblock);
@@ -430,12 +412,14 @@ static size_t build_udp_audio_packet(pa_raop_client *c, pa_memchunk *block, pa_m
 
     length = block->length;
     size = sizeof(udp_audio_header);
-    if (c->codec == PA_RAOP_CODEC_PCM)
-        size += write_PCM_data(((uint8_t *) buffer + head), packet->length - head, raw, &length);
-    else if (c->codec == PA_RAOP_CODEC_ALAC)
+    if (c->codec == PA_RAOP_CODEC_ALAC)
         size += write_ALAC_data(((uint8_t *) buffer + head), packet->length - head, raw, &length, false);
-    else
-        size += write_AAC_data(((uint8_t *) buffer + head), packet->length - head, raw, &length);
+    else {
+        pa_log_debug("Only ALAC encoding is supported, sending zeros...");
+        pa_memzero(((uint8_t *) buffer + head), packet->length - head);
+        size += length;
+    }
+
     c->rtptime += length / 4;
     c->seq++;
 

commit 750a677b43d509a2fb94171b3db12027531600cc
Author: Martin Blanchard <tchaik at gmx.com>
Date:   Sun Nov 6 12:54:10 2016 -0600

    raop: Rework packet's store memory management
    
    This patch switch the packet-buffer to use core memory pool instead of
    manually allocating the room required for storing TCP/UDP packets. Packets
    are now stored using pa_memchunk instead of internal struct. Quite a few
    malloc saved compare to previous design.

diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
index f1e5970..195b00c 100644
--- a/src/modules/raop/raop-client.c
+++ b/src/modules/raop/raop-client.c
@@ -65,6 +65,8 @@
 #define FRAMES_PER_TCP_PACKET 4096
 #define FRAMES_PER_UDP_PACKET 352
 
+#define RTX_BUFFERING_SECONDS 4
+
 #define DEFAULT_TCP_AUDIO_PORT   6000
 #define DEFAULT_UDP_AUDIO_PORT   6000
 #define DEFAULT_UDP_CONTROL_PORT 6001
@@ -323,92 +325,102 @@ static size_t write_AAC_data(uint8_t *packet, const size_t max, uint8_t *raw, si
     return size;
 }
 
-static size_t build_tcp_audio_packet(pa_raop_client *c, uint8_t *raw, size_t *index, size_t *length, uint32_t **packet) {
-    const size_t max = sizeof(tcp_audio_header) + 8 + 16384;
+static size_t build_tcp_audio_packet(pa_raop_client *c, pa_memchunk *block, pa_memchunk *packet) {
+    const size_t head = sizeof(tcp_audio_header);
     uint32_t *buffer = NULL;
-    size_t size, head;
+    uint8_t *raw = NULL;
+    size_t length, size;
 
-    *packet = NULL;
-    if (!(buffer = pa_xmalloc0(max)))
-        return 0;
+    raw = pa_memblock_acquire(block->memblock);
+    buffer = pa_memblock_acquire(packet->memblock);
+    buffer += packet->index / sizeof(uint32_t);
+    raw += block->index;
 
-    size = head = sizeof(tcp_audio_header);
+    c->seq++;
     memcpy(buffer, tcp_audio_header, sizeof(tcp_audio_header));
     buffer[1] |= htonl((uint32_t) c->seq);
     buffer[2] = htonl(c->rtptime);
     buffer[3] = htonl(c->ssrc);
 
+    length = block->length;
+    size = sizeof(tcp_audio_header);
     if (c->codec == PA_RAOP_CODEC_PCM)
-        size += write_PCM_data(((uint8_t *) buffer + head), max - head, raw, length);
+        size += write_PCM_data(((uint8_t *) buffer + head), packet->length - head, raw, &length);
     else if (c->codec == PA_RAOP_CODEC_ALAC)
-        size += write_ALAC_data(((uint8_t *) buffer + head), max - head, raw, length, false);
+        size += write_ALAC_data(((uint8_t *) buffer + head), packet->length - head, raw, &length, false);
     else
-        size += write_AAC_data(((uint8_t *) buffer + head), max - head, raw, length);
-    c->rtptime += *length / 4;
+        size += write_AAC_data(((uint8_t *) buffer + head), packet->length - head, raw, &length);
+    c->rtptime += length / 4;
+
+    pa_memblock_release(block->memblock);
 
     buffer[0] |= htonl((uint32_t) size - 4);
     if (c->encryption == PA_RAOP_ENCRYPTION_RSA)
         pa_raop_aes_encrypt(c->secret, (uint8_t *) buffer + head, size - head);
 
-    *packet = buffer;
+    pa_memblock_release(packet->memblock);
+    packet->length = size;
+
     return size;
 }
 
 static ssize_t send_tcp_audio_packet(pa_raop_client *c, pa_memchunk *block, size_t offset) {
-    static uint32_t * packet = NULL;
-    static size_t size, sent;
+    static int write_type = 0;
+    const size_t max = sizeof(tcp_audio_header) + 8 + 16384;
+    pa_memchunk *packet = NULL;
+    uint8_t *buffer = NULL;
     double progress = 0.0;
-    size_t index, length;
-    uint8_t *raw = NULL;
-    ssize_t written;
+    ssize_t written = -1;
+    size_t done = 0;
+
+    if (!(packet = pa_raop_packet_buffer_get(c->pbuf, c->seq, max)))
+        return -1;
 
-    if (!packet) {
-        index = block->index;
-        length = block->length;
-        raw = pa_memblock_acquire(block->memblock);
+    if (packet->length <= 0) {
+        pa_assert(block->index == offset);
 
-        pa_assert(raw);
-        pa_assert(index == offset);
-        pa_assert(length > 0);
+        if (!(packet = pa_raop_packet_buffer_get(c->pbuf, c->seq + 1, max)))
+            return -1;
 
-        size = build_tcp_audio_packet(c, raw, &index, &length, &packet);
-        sent = 0;
+        packet->index = 0;
+        packet->length = max;
+        if (!build_tcp_audio_packet(c, block, packet))
+            return -1;
     }
 
-    written = -1;
-    if (packet != NULL && size > 0)
-        written = pa_write(c->tcp_sfd, packet + sent, size - sent, NULL);
-    if (block->index == offset)
-        c->seq++;
-    if (sent == 0)
-        pa_memblock_release(block->memblock);
+    buffer = pa_memblock_acquire(packet->memblock);
+
+    pa_assert(buffer);
+
+    buffer += packet->index;
+    if (buffer && packet->length > 0)
+        written = pa_write(c->tcp_sfd, buffer, packet->length, &write_type);
     if (written > 0) {
-        sent += written;
-        progress = (double) sent / (double) size;
-        index = (block->index + block->length) * progress;
-        length = (block->index + block->length) - index;
-        block->length = length;
-        block->index = index;
-    }
+        progress = (double) written / (double) packet->length;
+        packet->length -= written;
+        packet->index += written;
 
-    if ((size - sent) <= 0) {
-        pa_xfree(packet);
-        packet = NULL;
+        done = block->length * progress;
+        block->length -= done;
+        block->index += done;
     }
 
+    pa_memblock_release(packet->memblock);
+
     return written;
 }
 
-static size_t build_udp_audio_packet(pa_raop_client *c, uint8_t *raw, size_t *index, size_t *length, uint32_t **packet) {
-    const size_t max = sizeof(udp_audio_header) + 8 + 1408;
+static size_t build_udp_audio_packet(pa_raop_client *c, pa_memchunk *block, pa_memchunk *packet) {
+    const size_t head = sizeof(udp_audio_header);
     uint32_t *buffer = NULL;
-    size_t size, head;
+    uint8_t *raw = NULL;
+    size_t length, size;
 
-    *packet = NULL;
-    if (!(buffer = pa_xmalloc0(max)))
-        return 0;
+    raw = pa_memblock_acquire(block->memblock);
+    buffer = pa_memblock_acquire(packet->memblock);
+    buffer += packet->index / sizeof(uint32_t);
+    raw += block->index;
 
-    size = head = sizeof(udp_audio_header);
     memcpy(buffer, udp_audio_header, sizeof(udp_audio_header));
     if (c->is_first_packet)
         buffer[0] |= htonl((uint32_t) 0x80 << 16);
@@ -416,75 +428,79 @@ static size_t build_udp_audio_packet(pa_raop_client *c, uint8_t *raw, size_t *in
     buffer[1] = htonl(c->rtptime);
     buffer[2] = htonl(c->ssrc);
 
+    length = block->length;
+    size = sizeof(udp_audio_header);
     if (c->codec == PA_RAOP_CODEC_PCM)
-        size += write_PCM_data(((uint8_t *) buffer + head), max - head, raw + *index, length);
+        size += write_PCM_data(((uint8_t *) buffer + head), packet->length - head, raw, &length);
     else if (c->codec == PA_RAOP_CODEC_ALAC)
-        size += write_ALAC_data(((uint8_t *) buffer + head), max - head, raw + *index, length, false);
+        size += write_ALAC_data(((uint8_t *) buffer + head), packet->length - head, raw, &length, false);
     else
-        size += write_AAC_data(((uint8_t *) buffer + head), max - head, raw + *index, length);
-    c->rtptime += *length / 4;
+        size += write_AAC_data(((uint8_t *) buffer + head), packet->length - head, raw, &length);
+    c->rtptime += length / 4;
+    c->seq++;
+
+    pa_memblock_release(block->memblock);
 
     if (c->encryption == PA_RAOP_ENCRYPTION_RSA)
         pa_raop_aes_encrypt(c->secret, (uint8_t *) buffer + head, size - head);
 
-    *index += *length;
-    *length = 0;
-    /* It is meaningless to preseve the partial data -> */
-    *packet = buffer;
+    pa_memblock_release(packet->memblock);
+    packet->length = size;
+
     return size;
 }
 
 static ssize_t send_udp_audio_packet(pa_raop_client *c, pa_memchunk *block, size_t offset) {
-    uint32_t *packet = NULL;
-    size_t index, length, size;
-    uint8_t *raw = NULL;
-    ssize_t written;
+    const size_t max = sizeof(udp_audio_retrans_header) + sizeof(udp_audio_header) + 8 + 1408;
+    pa_memchunk *packet = NULL;
+    uint8_t *buffer = NULL;
+    ssize_t written = -1;
 
-    index = block->index;
-    length = block->length;
-    raw = pa_memblock_acquire(block->memblock);
+    /* UDP packet has to be sent at once ! */
+    pa_assert(block->index == offset);
 
-    pa_assert(raw);
-    /* <- UDP packet has to be sent at once ! */
-    pa_assert(index == offset);
-    pa_assert(length > 0);
+    if (!(packet = pa_raop_packet_buffer_get(c->pbuf, c->seq, max)))
+        return -1;
 
-    written = -1;
-    size = build_udp_audio_packet(c, raw, &index, &length, &packet);
-    if (packet != NULL && size > 0)
-        written = pa_write(c->udp_sfd, packet, size, NULL);
-    if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
-        pa_log_debug("Discarding UDP (audio, seq=%d) packet due to EAGAIN (%s)", c->seq, pa_cstrerror(errno));
-    c->seq++;
+    packet->length = max;
+    packet->index = sizeof(udp_audio_retrans_header);
+    if (!build_udp_audio_packet(c, block, packet))
+        return -1;
 
-    /* Store packet for resending in the packet buffer (UDP). */
-    pa_raop_pb_write_packet(c->pbuf, c->seq, raw + block->index, block->length);
-    pa_xfree(packet);
+    buffer = pa_memblock_acquire(packet->memblock);
 
-    pa_memblock_release(block->memblock);
-    block->length = length;
-    block->index = index;
+    pa_assert(buffer);
+
+    buffer += packet->index;
+    if (buffer && packet->length > 0)
+        written = pa_write(c->udp_sfd, buffer, packet->length, NULL);
+    if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+        pa_log_debug("Discarding UDP (audio, seq=%d) packet due to EAGAIN (%s)", c->seq, pa_cstrerror(errno));
+        return (ssize_t) packet->length;
+    }
+
+    pa_memblock_release(packet->memblock);
+    /* It is meaningless to preseve the partial data */
+    block->index += block->length;
+    block->length = 0;
 
-    if (written < 0)
-        return (ssize_t) size;
     return written;
 }
 
-static size_t rebuild_udp_audio_packet(pa_raop_client *c, uint16_t seq, uint32_t **packet) {
+static size_t rebuild_udp_audio_packet(pa_raop_client *c, uint16_t seq, pa_memchunk *packet) {
     size_t size = sizeof(udp_audio_retrans_header);
     uint32_t *buffer = NULL;
-    uint8_t *data = NULL;
 
-    size += pa_raop_pb_read_packet(c->pbuf, seq, &data);
-    if (size == sizeof(udp_audio_retrans_header))
-        return 0;
-    if (!(buffer = pa_xmalloc0(size)))
-        return 0;
+    buffer = pa_memblock_acquire(packet->memblock);
 
     memcpy(buffer, udp_audio_retrans_header, sizeof(udp_audio_retrans_header));
     buffer[0] |= htonl((uint32_t) seq);
+    size += packet->length;
+
+    pa_memblock_release(packet->memblock);
+    packet->length += sizeof(udp_audio_retrans_header);
+    packet->index -= sizeof(udp_audio_retrans_header);
 
-    *packet = buffer;
     return size;
 }
 
@@ -493,20 +509,32 @@ static ssize_t resend_udp_audio_packets(pa_raop_client *c, uint16_t seq, uint16_
     int i = 0;
 
     for (i = 0; i < nbp; i++) {
-        uint32_t * packet = NULL;
-        ssize_t written = 0;
-        size_t size = 0;
+        pa_memchunk *packet = NULL;
+        uint8_t *buffer = NULL;
+        ssize_t written = -1;
+
+        if (!(packet = pa_raop_packet_buffer_get(c->pbuf, seq + i, 0)))
+            continue;
+
+        if (packet->index > 0) {
+            if (!rebuild_udp_audio_packet(c, seq + i, packet))
+                continue;
+        }
+
+        pa_assert(packet->index == 0);
+
+        buffer = pa_memblock_acquire(packet->memblock);
 
-        size = rebuild_udp_audio_packet(c, seq, &packet);
-        if (packet != NULL && size > 0)
-            written = pa_write(c->udp_cfd, packet, size, NULL);
+        pa_assert(buffer);
+
+        if (buffer && packet->length > 0)
+            written = pa_write(c->udp_cfd, buffer, packet->length, NULL);
         if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
-            pa_log_debug("Discarding UDP (audio-restransmitted, seq=%d) packet due to EAGAIN", c->seq);
+            pa_log_debug("Discarding UDP (audio-restransmitted, seq=%d) packet due to EAGAIN", seq + i);
             continue;
         }
 
-        if (written > 0)
-            total +=  written;
+        total +=  written;
     }
 
     return total;
@@ -557,17 +585,13 @@ static size_t handle_udp_control_packet(pa_raop_client *c, const uint8_t packet[
     ssize_t written = 0;
 
     /* Control packets are 8 bytes long:  */
-    if (size != 8 || packet[0] != 0x80) {
-        pa_log_debug("Received an invalid control packet...");
+    if (size != 8 || packet[0] != 0x80)
         return 1;
-    }
 
     seq = ntohs((uint16_t) packet[4]);
     nbp = ntohs((uint16_t) packet[6]);
-    if (nbp <= 0) {
-        pa_log_debug("Received an invalid control packet...");
+    if (nbp <= 0)
         return 1;
-    }
 
     /* The market bit is always set (see rfc3550 for packet structure) ! */
     payload = packet[1] ^ 0x80;
@@ -631,10 +655,8 @@ static size_t handle_udp_timing_packet(pa_raop_client *c, const uint8_t packet[]
     uint64_t rci = 0;
 
     /* Timing packets are 32 bytes long: 1 x 8 RTP header (no ssrc) + 3 x 8 NTP timestamps */
-    if (size != 32 || packet[0] != 0x80) {
-        pa_log_debug("Received an invalid UDP timing packet...");
+    if (size != 32 || packet[0] != 0x80)
         return 0;
-    }
 
     rci = timeval_to_ntp(pa_rtclock_get(&tv));
     data = (uint32_t *) (packet + sizeof(udp_timming_header));
@@ -1061,6 +1083,8 @@ static void rtsp_stream_cb(pa_rtsp_client *rtsp, pa_rtsp_state_t state, pa_rtsp_
             if (alt)
                 pa_atoi(alt, &latency);
 
+            pa_raop_packet_buffer_reset(c->pbuf, c->seq);
+
             pa_random(&ssrc, sizeof(ssrc));
             c->is_first_packet = true;
             c->is_recording = true;
@@ -1093,9 +1117,6 @@ static void rtsp_stream_cb(pa_rtsp_client *rtsp, pa_rtsp_state_t state, pa_rtsp_
 
             c->is_recording = false;
 
-            if (c->pbuf)
-                pa_raop_pb_clear(c->pbuf);
-
             if (c->tcp_sfd > 0)
                 pa_close(c->tcp_sfd);
             c->tcp_sfd = -1;
@@ -1124,9 +1145,6 @@ static void rtsp_stream_cb(pa_rtsp_client *rtsp, pa_rtsp_state_t state, pa_rtsp_
 
             c->is_recording = false;
 
-            if (c->pbuf)
-                pa_raop_pb_clear(c->pbuf);
-
             if (c->tcp_sfd > 0)
                 pa_close(c->tcp_sfd);
             c->tcp_sfd = -1;
@@ -1331,6 +1349,7 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_prot
 
     pa_parsed_address a;
     pa_sample_spec ss;
+    size_t size = 2;
 
     pa_assert(core);
     pa_assert(host);
@@ -1369,6 +1388,8 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_prot
         c->secret = pa_raop_secret_new();
 
     ss = core->default_sample_spec;
+    if (c->protocol == PA_RAOP_PROTOCOL_UDP)
+        size = RTX_BUFFERING_SECONDS * ss.rate / FRAMES_PER_UDP_PACKET;
 
     c->is_recording = false;
     c->is_first_packet = true;
@@ -1376,7 +1397,7 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_prot
     c->sync_interval = ss.rate / FRAMES_PER_UDP_PACKET;
     c->sync_count = 0;
 
-    c->pbuf = pa_raop_pb_new(UDP_DEFAULT_PKT_BUF_SIZE);
+    c->pbuf = pa_raop_packet_buffer_new(c->core->mempool, size);
 
     return c;
 }
@@ -1384,7 +1405,7 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_prot
 void pa_raop_client_free(pa_raop_client *c) {
     pa_assert(c);
 
-    pa_raop_pb_delete(c->pbuf);
+    pa_raop_packet_buffer_free(c->pbuf);
 
     pa_xfree(c->sid);
     pa_xfree(c->sci);
@@ -1686,10 +1707,10 @@ void pa_raop_client_handle_oob_packet(pa_raop_client *c, const int fd, const uin
 
     if (c->protocol == PA_RAOP_PROTOCOL_UDP) {
         if (fd == c->udp_cfd) {
-            pa_log_debug("Received UDP control packet");
+            pa_log_debug("Received UDP control packet...");
             handle_udp_control_packet(c, packet, size);
         } else if (fd == c->udp_tfd) {
-            pa_log_debug("Received UDP timing packet");
+            pa_log_debug("Received UDP timing packet...");
             handle_udp_timing_packet(c, packet, size);
         }
     }
diff --git a/src/modules/raop/raop-packet-buffer.c b/src/modules/raop/raop-packet-buffer.c
index d9d8549..06e3125 100644
--- a/src/modules/raop/raop-packet-buffer.c
+++ b/src/modules/raop/raop-packet-buffer.c
@@ -20,152 +20,99 @@
   USA.
 ***/
 
-#include <stdlib.h>
-#include <limits.h>
-
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
+#include <stdlib.h>
+#include <limits.h>
+
+#include <pulse/xmalloc.h>
+
 #include <pulsecore/core-error.h>
-#include "raop-client.h"
+#include <pulsecore/macro.h>
 
 #include "raop-packet-buffer.h"
 
-/* FRAMES_PER_PACKET*2*2 + sizeof(udp_audio_header) + sizeof(ALAC header), unencoded */
-#define PACKET_SIZE_MAX (352*2*2 + 12 + 7) /* FIXME; hardcoded constant ! */
-/* Header room for packet retransmission header */
-#define RETRANS_HEADER_ROOM 4
-
-/* Packet element */
-struct pa_raop_packet_element {
-    uint16_t  seq_num; /* RTP sequence number (in host byte order) */
-    ssize_t   length;  /* Actual packet length */
-    /* Packet data including RTP header */
-    uint8_t   data[PACKET_SIZE_MAX + RETRANS_HEADER_ROOM];
-};
-
-/* Buffer struct */
 struct pa_raop_packet_buffer {
-    size_t   size;          /* max number of packets in buffer */
-    size_t   start;         /* index of oldest packet */
-    size_t   count;         /* number of packets in buffer */
-    uint16_t first_seq_num; /* Sequence number of first packet in buffer */
-    uint16_t latest_seq_num; /* Debug purpose */
-    pa_raop_packet_element *packets; /* Packet element pointer */
+    pa_memchunk *packets;
+    pa_mempool *mempool;
+    size_t size;
+
+    uint16_t seq;
+    size_t pos;
 };
 
-pa_raop_packet_buffer *pa_raop_pb_new(size_t size) {
-    pa_raop_packet_buffer *pb = pa_xmalloc0(sizeof(*pb));
+pa_raop_packet_buffer *pa_raop_packet_buffer_new(pa_mempool *mempool, const size_t size) {
+    pa_raop_packet_buffer *pb = pa_xnew0(pa_raop_packet_buffer, 1);
 
-    pb->size = size;
-    pb->packets = (pa_raop_packet_element *)
-        pa_xmalloc(size * sizeof(pa_raop_packet_element));
+    pa_assert(mempool);
+    pa_assert(size > 0);
 
-    pa_raop_pb_clear(pb);
+    pb->size = size;
+    pb->mempool = mempool;
+    pb->packets = pa_xnew0(pa_memchunk, size);
+    pb->seq = pb->pos = 0;
 
     return pb;
 }
 
-void pa_raop_pb_clear(pa_raop_packet_buffer *pb) {
-    pb->start = 0;
-    pb->count = 0;
-    pb->first_seq_num = 0;
-    pb->latest_seq_num = 0;
-    memset(pb->packets, 0, pb->size * sizeof(pa_raop_packet_element));
-}
-
-void pa_raop_pb_delete(pa_raop_packet_buffer *pb) {
-    pa_xfree(pb->packets);
-    pa_xfree(pb);
-}
-
-static int pb_is_full(pa_raop_packet_buffer *pb) {
-    return pb->count == pb->size;
-}
-
-static int pb_is_empty(pa_raop_packet_buffer *pb) {
-    return pb->count == 0;
-}
-
-static pa_raop_packet_element *pb_prepare_write(pa_raop_packet_buffer *pb, uint16_t seq) {
-    size_t end = (pb->start + pb->count) % pb->size;
-    pa_raop_packet_element *packet;
-
-    /* Set first packet sequence number in buffer if buffer is empty */
-    if (pb_is_empty(pb))
-        pb->first_seq_num = seq;
-    else
-        pa_assert((uint16_t) (pb->latest_seq_num + 1) == seq);
-
-    packet = &pb->packets[end];
-
-    if (pb_is_full(pb)) {
-        pb->start = (pb->start + 1) % pb->size; /* full, overwrite */
+void pa_raop_packet_buffer_free(pa_raop_packet_buffer *pb) {
+    size_t i;
 
-        /* Set first packet sequence number in buffer
-           to new start packet sequence number */
-        pb->first_seq_num = pb->packets[pb->start].seq_num;
-    } else
-        ++ pb->count;
+    pa_assert(pb);
 
-    pb->latest_seq_num = seq;
+    for (i = 0; pb->packets && i < pb->size; i++) {
+        if (pb->packets[i].memblock)
+            pa_memblock_unref(pb->packets[i].memblock);
+        pa_memchunk_reset(&pb->packets[i]);
+    }
 
-    return packet;
+    pa_xfree(pb->packets);
+    pb->packets = NULL;
+    pa_xfree(pb);
 }
 
-/* Write packet data to packet buffer */
-void pa_raop_pb_write_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, const uint8_t *packet_data, ssize_t packet_length) {
-    pa_raop_packet_element *packet;
+void pa_raop_packet_buffer_reset(pa_raop_packet_buffer *pb, uint16_t seq) {
+    size_t i;
 
     pa_assert(pb);
-    pa_assert(packet_data);
-    pa_assert(packet_length <= PACKET_SIZE_MAX);
-
-    packet = pb_prepare_write(pb, seq_num);
-    packet->seq_num = seq_num;
-    packet->length = packet_length + RETRANS_HEADER_ROOM;
-
-    /* Insert RETRANS_HEADER_ROOM bytes in front of packet data,
-       for retransmission header */
-    memset(packet->data, 0, RETRANS_HEADER_ROOM);
-    memcpy(packet->data + RETRANS_HEADER_ROOM, packet_data, packet_length);
-}
-
-/* l < r?, considers wrapping */
-static bool seq_lt(uint16_t l, uint16_t r) {
-    return l - r > USHRT_MAX/2;
+    pa_assert(pb->packets);
+
+    pb->pos = 0;
+    pb->seq = seq - 1;
+    for (i = 0; i < pb->size; i++) {
+        if (pb->packets[i].memblock)
+            pa_memblock_unref(pb->packets[i].memblock);
+        pa_memchunk_reset(&pb->packets[i]);
+    }
 }
 
-/* Random access to packet from buffer by sequence number for (re-)sending. */
-ssize_t pa_raop_pb_read_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, uint8_t **packet_data) {
-    uint16_t index = 0; /* Index of requested packet */
-    pa_raop_packet_element *packet;
-
-    /* If the buffer is empty, there is no use in calculating indices */
-    if (pb_is_empty(pb))
-        return -1;
-
-    /* If the requested packet is too old (seq_num below first seq number
-       in buffer) or too young (seq_num greater than current seq number),
-       do nothing and return */
-    if (seq_lt(seq_num, pb->first_seq_num))
-        return -1;
+pa_memchunk *pa_raop_packet_buffer_get(pa_raop_packet_buffer *pb, uint16_t seq, const size_t size) {
+    pa_memchunk *packet = NULL;
+    size_t delta, i;
 
-    index = (uint16_t) (seq_num - pb->first_seq_num);
-    if (index >= pb->count)
-        return -1;
-
-    /*  Index of the requested packet in the buffer is calculated
-        using the first sequence number stored in the buffer.
-        The offset (seq_num - first_seq_num) is used to access the array. */
-    packet = &pb->packets[(pb->start + index) % pb->size];
-
-    pa_assert(packet->data[RETRANS_HEADER_ROOM + 2] == (seq_num >> 8));
-    pa_assert(packet->data[RETRANS_HEADER_ROOM + 3] == (seq_num & 0xff));
-    pa_assert(packet_data);
-
-    *packet_data = packet->data;
+    pa_assert(pb);
+    pa_assert(pb->packets);
+    pa_assert(seq > 0);
+
+    if (seq == pb->seq)
+        packet = &pb->packets[pb->pos];
+    else if (seq < pb->seq) {
+        delta = pb->seq - seq;
+        i = (pb->size + pb->pos - delta) % pb->size;
+        if (delta < pb->size)
+            packet = &pb->packets[i];
+    } else {
+        i = (pb->pos + (seq - pb->seq)) % pb->size;
+        if (pb->packets[i].memblock)
+            pa_memblock_unref(pb->packets[i].memblock);
+        pa_memchunk_reset(&pb->packets[i]);
+        pb->packets[i].memblock = pa_memblock_new(pb->mempool, size);
+        packet = &pb->packets[i];
+        pb->seq = seq;
+        pb->pos = i;
+    }
 
-    return packet->length;
+    return packet;
 }
diff --git a/src/modules/raop/raop-packet-buffer.h b/src/modules/raop/raop-packet-buffer.h
index 69a0ce1..b8c7617 100644
--- a/src/modules/raop/raop-packet-buffer.h
+++ b/src/modules/raop/raop-packet-buffer.h
@@ -23,19 +23,16 @@
   USA.
 ***/
 
-struct pa_raop_packet_element;
-typedef struct pa_raop_packet_element pa_raop_packet_element;
+#include <pulsecore/memblock.h>
+#include <pulsecore/memchunk.h>
 
-struct pa_raop_packet_buffer;
 typedef struct pa_raop_packet_buffer pa_raop_packet_buffer;
 
-/* Allocates a new circular packet buffer
-   size: Maximum number of packets to store */
-pa_raop_packet_buffer *pa_raop_pb_new(size_t size);
-void pa_raop_pb_clear(pa_raop_packet_buffer *pb);
-void pa_raop_pb_delete(pa_raop_packet_buffer *pb);
+/* Allocates a new circular packet buffer, size: Maximum number of packets to store */
+pa_raop_packet_buffer *pa_raop_packet_buffer_new(pa_mempool *mempool, const size_t size);
+void pa_raop_packet_buffer_free(pa_raop_packet_buffer *pb);
 
-void pa_raop_pb_write_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, const uint8_t *packet_data, ssize_t packet_length);
-ssize_t pa_raop_pb_read_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, uint8_t **packet_data);
+void pa_raop_packet_buffer_reset(pa_raop_packet_buffer *pb, uint16_t seq);
+pa_memchunk *pa_raop_packet_buffer_get(pa_raop_packet_buffer *pb, uint16_t seq, const size_t size);
 
 #endif
diff --git a/src/modules/raop/raop-sink.c b/src/modules/raop/raop-sink.c
index 35eadce..54fda5f 100644
--- a/src/modules/raop/raop-sink.c
+++ b/src/modules/raop/raop-sink.c
@@ -333,7 +333,6 @@ static void thread_func(void *userdata) {
         unsigned int i, nbfds = 0;
         pa_usec_t now, estimated, intvl;
         uint64_t position;
-        ssize_t written;
         size_t index;
         int ret;
 
@@ -399,8 +398,7 @@ static void thread_func(void *userdata) {
         pa_assert(u->memchunk.length > 0);
 
         index = u->memchunk.index;
-        written = pa_raop_client_send_audio_packet(u->raop, &u->memchunk, offset);
-        if (written < 0) {
+        if (pa_raop_client_send_audio_packet(u->raop, &u->memchunk, offset) < 0) {
             if (errno == EINTR) {
                 /* Just try again. */
                 pa_log_debug("Failed to write data to FIFO (EINTR), retrying");

commit 32a5b3047b573268c723097ee09d176275d83c11
Author: Martin Blanchard <tchaik at gmx.com>
Date:   Sun Nov 6 12:54:09 2016 -0600

    raop: Update and standardise source file headers

diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
index 813b161..f1e5970 100644
--- a/src/modules/raop/raop-client.c
+++ b/src/modules/raop/raop-client.c
@@ -2,6 +2,8 @@
   This file is part of PulseAudio.
 
   Copyright 2008 Colin Guthrie
+  Copyright 2013 Hajime Fujita
+  Copyright 2013 Martin Blanchard
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
diff --git a/src/modules/raop/raop-packet-buffer.c b/src/modules/raop/raop-packet-buffer.c
index ef8ea15..d9d8549 100644
--- a/src/modules/raop/raop-packet-buffer.c
+++ b/src/modules/raop/raop-packet-buffer.c
@@ -1,15 +1,15 @@
 /***
-  Circular buffer for RTP audio packets with random access support
-  by RTP sequence number.
+  This file is part of PulseAudio.
 
-  Copyright 2013 Matthias Wabersich, Hajime Fujita
+  Copyright 2013 Matthias Wabersich
+  Copyright 2013 Hajime Fujita
 
-  This is free software; you can redistribute it and/or modify
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2.1 of the License,
   or (at your option) any later version.
 
-  This is distributed in the hope that it will be useful, but
+  PulseAudio 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.
@@ -18,7 +18,6 @@
   along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
-
 ***/
 
 #include <stdlib.h>
diff --git a/src/modules/raop/raop-packet-buffer.h b/src/modules/raop/raop-packet-buffer.h
index d8a08a0..69a0ce1 100644
--- a/src/modules/raop/raop-packet-buffer.h
+++ b/src/modules/raop/raop-packet-buffer.h
@@ -1,18 +1,18 @@
-#ifndef RAOP_PACKET_BUFFER_H_INCLUDED
-#define RAOP_PACKET_BUFFER_H_INCLUDED
+#ifndef fooraoppacketbufferfoo
+#define fooraoppacketbufferfoo
 
 /***
-  Circular buffer for RTP audio packets with random access support
-  by RTP sequence number.
+  This file is part of PulseAudio.
 
-  Copyright 2013 Matthias Wabersich, Hajime Fujita
+  Copyright 2013 Matthias Wabersich
+  Copyright 2013 Hajime Fujita
 
-  This is free software; you can redistribute it and/or modify
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2.1 of the License,
   or (at your option) any later version.
 
-  This is distributed in the hope that it will be useful, but
+  PulseAudio 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.
@@ -21,7 +21,6 @@
   along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
-
 ***/
 
 struct pa_raop_packet_element;
@@ -39,4 +38,4 @@ void pa_raop_pb_delete(pa_raop_packet_buffer *pb);
 void pa_raop_pb_write_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, const uint8_t *packet_data, ssize_t packet_length);
 ssize_t pa_raop_pb_read_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, uint8_t **packet_data);
 
-#endif /* RAOP_PACKET_BUFFER_H_INCLUDED */
+#endif
diff --git a/src/modules/raop/raop-sink.c b/src/modules/raop/raop-sink.c
index fdec0f2..35eadce 100644
--- a/src/modules/raop/raop-sink.c
+++ b/src/modules/raop/raop-sink.c
@@ -3,6 +3,7 @@
 
   Copyright 2004-2006 Lennart Poettering
   Copyright 2008 Colin Guthrie
+  Copyright 2013 Hajime Fujita
   Copyright 2013 Martin Blanchard
 
   PulseAudio is free software; you can redistribute it and/or modify
diff --git a/src/modules/raop/raop-util.c b/src/modules/raop/raop-util.c
index 3e3eb00..febc204 100644
--- a/src/modules/raop/raop-util.c
+++ b/src/modules/raop/raop-util.c
@@ -1,8 +1,9 @@
 /***
   This file is part of PulseAudio.
 
+  Copyright 2008 Colin Guthrie
+  Copyright Kungliga Tekniska högskolan
   Copyright 2013 Martin Blanchard
-  Copyright Kungliga Tekniska Høgskolan & Colin Guthrie
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published

commit 094906b3f9de5d2fda1e61e5aeec23fecf34efd4
Author: Martin Blanchard <tchaik at gmx.com>
Date:   Sun Nov 6 12:54:08 2016 -0600

    raop: Prefer ALAC encoding to raw PCM if supported by server
    
    ALAC encoding is to be prefered simply because ALAC audio packet reverse-
    engineering and implementation is in better shape than raw PCM. Sending ALAC
    audio does not mean compressing audio and thus linking an external library to
    do so. ALAC packets has the ability to carry uncompressed PCM frames, and
    that's what is implemented at the time.

diff --git a/src/modules/raop/module-raop-discover.c b/src/modules/raop/module-raop-discover.c
index 6e2542a..bf05dec 100644
--- a/src/modules/raop/module-raop-discover.c
+++ b/src/modules/raop/module-raop-discover.c
@@ -200,7 +200,10 @@ static void resolver_cb(
              *  - 1 = ALAC,
              *  - 2 = AAC,
              *  - 3 = AAC ELD. */
-            cn = pa_xstrdup("PCM");
+            if (pa_str_in_list(value, ",", "1"))
+                cn = pa_xstrdup("ALAC");
+            else
+                cn = pa_xstrdup("PCM");
         } else if (pa_streq(key, "md")) {
             /* Supported metadata types:
              *  - 0 = text,

commit 8022e565810af61ffea436fcad8ef8085fc912aa
Author: Martin Blanchard <tchaik at gmx.com>
Date:   Sun Nov 6 12:54:07 2016 -0600

    raop: Merge TCP and UDP code paths + refactoring
    
    TCP and UDP implementation are following two diffrent code path while code
    logic is quite the same. This patch merges both code path into a unique one
    and, thus, leads to a big refactoring. Major changes include:
      - moving sink implementation to a separate file (raop-sink.c)
      - move raop-sink.c protocol specific code to raop-client.c
      - modernise RTSP session handling in TCP mode
      - reduce code duplications between TCP and UDP modes
      - introduce authentication support
      - TCP mode does not constantly send silent audio anymore
    
    About authentication: OPTIONS is now issued when the sink is preliminary
    loaded. Client authentication appends at that time and credential is kept
    for the whole sink lifetime. Later RTSP connection will thus look like this:
    ANNOUNCE > 200 OK > SETUP > 200 OK > RECORD > 200 OK (no more OPTIONS). This
    behaviour is similar to iTunes one.
    
    Also this patch includes file name changes to match Pulseaudio naming
    rules, as most of pulseaudio source code files seem to be using '-'
    instead of '_' as a word separator.

diff --git a/src/Makefile.am b/src/Makefile.am
index c3ddc86..5b723e6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1154,10 +1154,12 @@ librtp_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
 librtp_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINOR@.la libpulsecommon- at PA_MAJORMINOR@.la libpulse.la
 
 libraop_la_SOURCES = \
-        modules/raop/raop_client.c modules/raop/raop_client.h \
-        modules/raop/raop_packet_buffer.h modules/raop/raop_packet_buffer.c \
-        modules/raop/raop_crypto.c modules/raop/raop_crypto.h \
-        modules/raop/raop_util.c modules/raop/raop_util.h
+        modules/raop/raop-util.c modules/raop/raop-util.h \
+        modules/raop/raop-crypto.c modules/raop/raop-crypto.h \
+        modules/raop/raop-packet-buffer.h modules/raop/raop-packet-buffer.c \
+        modules/raop/raop-client.c modules/raop/raop-client.h \
+        modules/raop/raop-sink.c modules/raop/raop-sink.h
+
 libraop_la_CFLAGS = $(AM_CFLAGS) $(OPENSSL_CFLAGS) -I$(top_srcdir)/src/modules/rtp
 libraop_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
 libraop_la_LIBADD = $(AM_LIBADD) $(OPENSSL_LIBS) libpulsecore- at PA_MAJORMINOR@.la librtp.la libpulsecommon- at PA_MAJORMINOR@.la libpulse.la
diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c
index e251f2f..82fa48d 100644
--- a/src/modules/raop/module-raop-sink.c
+++ b/src/modules/raop/module-raop-sink.c
@@ -22,107 +22,34 @@
 #include <config.h>
 #endif
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <sys/ioctl.h>
-
-#ifdef HAVE_LINUX_SOCKIOS_H
-#include <linux/sockios.h>
-#endif
-
-#include <pulse/rtclock.h>
-#include <pulse/timeval.h>
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/core-error.h>
-#include <pulsecore/sink.h>
 #include <pulsecore/module.h>
-#include <pulsecore/core-util.h>
+#include <pulsecore/sink.h>
 #include <pulsecore/modargs.h>
-#include <pulsecore/log.h>
-#include <pulsecore/socket-client.h>
-#include <pulsecore/thread-mq.h>
-#include <pulsecore/thread.h>
-#include <pulsecore/time-smoother.h>
-#include <pulsecore/poll.h>
+
+#include "raop-sink.h"
 
 #include "module-raop-sink-symdef.h"
-#include "rtp.h"
-#include "sdp.h"
-#include "sap.h"
-#include "raop_client.h"
 
 PA_MODULE_AUTHOR("Colin Guthrie");
 PA_MODULE_DESCRIPTION("RAOP Sink");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(false);
 PA_MODULE_USAGE(
+        "name=<name of the sink, to be prefixed> "
         "sink_name=<name for the sink> "
         "sink_properties=<properties for the sink> "
-        "server=<address>  "
+        "server=<address> "
         "protocol=<transport protocol> "
         "encryption=<encryption type> "
         "codec=<audio codec> "
         "format=<sample format> "
         "rate=<sample rate> "
-        "channels=<number of channels>");
-
-struct userdata {
-    pa_core *core;
-    pa_module *module;
-    pa_sink *sink;
-
-    pa_thread_mq thread_mq;
-    pa_rtpoll *rtpoll;
-    pa_rtpoll_item *rtpoll_item;
-    pa_thread *thread;
-
-    pa_raop_protocol_t protocol;
-
-    pa_memchunk raw_memchunk;
-    pa_memchunk encoded_memchunk;
-
-    void *write_data;
-    size_t write_length, write_index;
-
-    void *read_data;
-    size_t read_length, read_index;
-
-    pa_usec_t latency;
-
-    /*esd_format_t format;*/
-    int32_t rate;
-
-    pa_smoother *smoother;
-
-    int64_t offset;
-    int64_t encoding_overhead;
-    int32_t next_encoding_overhead;
-    double encoding_ratio;
-
-    pa_raop_client *raop;
-
-    size_t block_size;
-
-    /* Members only for the TCP protocol */
-    int tcp_fd;
-
-    /* Members only for the UDP protocol */
-    int udp_control_fd;
-    int udp_timing_fd;
-
-    /* For UDP thread wakeup clock calculation */
-    pa_usec_t udp_playback_start;
-    uint32_t  udp_sent_packets;
-};
+        "channels=<number of channels> "
+        "username=<authentication user name, default: \"iTunes\"> "
+        "password=<authentication password>");
 
 static const char* const valid_modargs[] = {
+    "name",
     "sink_name",
     "sink_properties",
     "server",
@@ -132,864 +59,29 @@ static const char* const valid_modargs[] = {
     "format",
     "rate",
     "channels",
+    "username",
+    "password",
     NULL
 };
 
-enum {
-    SINK_MESSAGE_TCP_PASS_SOCKET = PA_SINK_MESSAGE_MAX,
-    SINK_MESSAGE_TCP_RIP_SOCKET,
-    SINK_MESSAGE_UDP_SETUP,
-    SINK_MESSAGE_UDP_RECORD,
-    SINK_MESSAGE_UDP_DISCONNECTED,
-};
-
-/* Forward declarations: */
-static void sink_set_volume_cb(pa_sink *);
-
-static void tcp_on_connection(int fd, void *userdata) {
-    int so_sndbuf = 0;
-    socklen_t sl = sizeof(int);
-    struct userdata *u = userdata;
-    pa_assert(u);
-
-    pa_assert(u->tcp_fd < 0);
-    u->tcp_fd = fd;
-
-    if (getsockopt(u->tcp_fd, SOL_SOCKET, SO_SNDBUF, &so_sndbuf, &sl) < 0)
-        pa_log_warn("getsockopt(SO_SNDBUF) failed: %s", pa_cstrerror(errno));
-    else {
-        pa_log_debug("SO_SNDBUF is %zu.", (size_t) so_sndbuf);
-        pa_sink_set_max_request(u->sink, PA_MAX((size_t) so_sndbuf, u->block_size));
-    }
-
-    /* Set the initial volume. */
-    sink_set_volume_cb(u->sink);
-
-    pa_log_debug("Connection authenticated, handing fd to IO thread...");
-
-    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_TCP_PASS_SOCKET, NULL, 0, NULL, NULL);
-}
-
-static void tcp_on_close(void*userdata) {
-    struct userdata *u = userdata;
-    pa_assert(u);
-
-    pa_log_debug("Connection closed, informing IO thread...");
-
-    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_TCP_RIP_SOCKET, NULL, 0, NULL, NULL);
-}
-
-static pa_usec_t sink_get_latency(const struct userdata *u) {
-    pa_usec_t w, r;
-
-    r = pa_smoother_get(u->smoother, pa_rtclock_now());
-    w = pa_bytes_to_usec((u->offset - u->encoding_overhead + (u->encoded_memchunk.length / u->encoding_ratio)), &u->sink->sample_spec);
-
-    return w > r ? w - r : 0;
-}
-
-static int tcp_sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
-    struct userdata *u = PA_SINK(o)->userdata;
-
-    switch (code) {
-
-        case PA_SINK_MESSAGE_SET_STATE:
-
-            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
-
-                case PA_SINK_SUSPENDED:
-                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
-
-                    pa_smoother_pause(u->smoother, pa_rtclock_now());
-
-                    /* Issue a FLUSH if we are connected. */
-                    if (u->tcp_fd >= 0) {
-                        pa_raop_client_flush(u->raop);
-                    }
-                    break;
-
-                case PA_SINK_IDLE:
-                case PA_SINK_RUNNING:
-
-                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
-                        pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
-
-                        /* The connection can be closed when idle, so check to
-                         * see if we need to reestablish it. */
-                        if (u->tcp_fd < 0)
-                            pa_raop_client_connect(u->raop);
-                        else
-                            pa_raop_client_flush(u->raop);
-                    }
-
-                    break;
-
-                case PA_SINK_UNLINKED:
-                case PA_SINK_INIT:
-                case PA_SINK_INVALID_STATE:
-                    ;
-            }
-
-            break;
-
-        case PA_SINK_MESSAGE_GET_LATENCY: {
-            *((pa_usec_t*) data) = sink_get_latency(u);
-            return 0;
-        }
-
-        case SINK_MESSAGE_TCP_PASS_SOCKET: {
-            struct pollfd *pollfd;
-
-            pa_assert(!u->rtpoll_item);
-
-            u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
-            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
-            pollfd->fd = u->tcp_fd;
-            pollfd->events = POLLOUT;
-            /*pollfd->events = */pollfd->revents = 0;
-
-            if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
-                /* Our stream has been suspended so we just flush it... */
-                pa_raop_client_flush(u->raop);
-            }
-            return 0;
-        }
-
-        case SINK_MESSAGE_TCP_RIP_SOCKET: {
-            if (u->tcp_fd >= 0) {
-                pa_close(u->tcp_fd);
-                u->tcp_fd = -1;
-            } else
-                /* FIXME */
-                pa_log("We should not get to this state. Cannot rip socket if not connected.");
-
-            if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
-
-                pa_log_debug("RTSP control connection closed, but we're suspended so let's not worry about it... we'll open it again later");
-
-                if (u->rtpoll_item)
-                    pa_rtpoll_item_free(u->rtpoll_item);
-                u->rtpoll_item = NULL;
-            } else {
-                /* Question: is this valid here: or should we do some sort of:
-                   return pa_sink_process_msg(PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL);
-                   ?? */
-                pa_module_unload_request(u->module, true);
-            }
-            return 0;
-        }
-    }
-
-    return pa_sink_process_msg(o, code, data, offset, chunk);
-}
-
-static void udp_start_wakeup_clock(struct userdata *u) {
-    pa_usec_t now = pa_rtclock_now();
-
-    u->udp_playback_start = now;
-    u->udp_sent_packets = 0;
-    pa_rtpoll_set_timer_absolute(u->rtpoll, now);
-}
-
-static pa_usec_t udp_next_wakeup_clock(struct userdata *u) {
-    pa_usec_t intvl = pa_bytes_to_usec(u->block_size * u->udp_sent_packets,
-                                       &u->sink->sample_spec);
-    /* FIXME: how long until (u->block_size * u->udp_sent_packets) wraps?? */
-
-    return u->udp_playback_start + intvl;
-}
-
-static int udp_sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
-    struct userdata *u = PA_SINK(o)->userdata;
-
-    switch (code) {
-        case PA_SINK_MESSAGE_SET_STATE:
-            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
-                case PA_SINK_SUSPENDED:
-                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
-                    pa_log_debug("RAOP: SUSPENDED");
-                    pa_smoother_pause(u->smoother, pa_rtclock_now());
-
-                    if (pa_raop_client_udp_is_alive(u->raop)) {
-                        /* Issue a TEARDOWN if we are still connected. */
-                        pa_raop_client_teardown(u->raop);
-                    }
-
-                    break;
-
-                case PA_SINK_IDLE:
-                    pa_log_debug("RAOP: IDLE");
-                    /* Issue a flush if we're comming from running state. */
-                    if (u->sink->thread_info.state == PA_SINK_RUNNING) {
-                        pa_rtpoll_set_timer_disabled(u->rtpoll);
-                        pa_raop_client_flush(u->raop);
-                    }
-
-                    break;
-
-                case PA_SINK_RUNNING:
-                    pa_log_debug("RAOP: RUNNING");
-
-                    pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
-
-                    if (!pa_raop_client_udp_is_alive(u->raop)) {
-                        /* Connecting will trigger a RECORD and start steaming */
-                        pa_raop_client_connect(u->raop);
-                    } else if (!pa_raop_client_udp_can_stream(u->raop)) {
-                        /* RECORD alredy sent, simply start streaming */
-                        pa_raop_client_udp_stream(u->raop);
-                    }
-
-                    udp_start_wakeup_clock(u);
-
-                    break;
-
-                case PA_SINK_UNLINKED:
-                case PA_SINK_INIT:
-                case PA_SINK_INVALID_STATE:
-                    ;
-            }
-
-            break;
-
-        case PA_SINK_MESSAGE_GET_LATENCY: {
-            pa_usec_t r = 0;
-
-            if (pa_raop_client_udp_can_stream(u->raop))
-                r = sink_get_latency(u);
-
-            *((pa_usec_t*) data) = r;
-
-            return 0;
-        }
-
-        case SINK_MESSAGE_UDP_SETUP: {
-            struct pollfd *pollfd;
-
-            u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 2);
-            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
-
-            pollfd->fd = u->udp_control_fd;
-            pollfd->events = POLLIN | POLLPRI;
-            pollfd->revents = 0;
-            pollfd++;
-            pollfd->fd = u->udp_timing_fd;
-            pollfd->events = POLLIN | POLLPRI;
-            pollfd->revents = 0;
-
-            return 0;
-        }
-
-        case SINK_MESSAGE_UDP_RECORD: {
-            udp_start_wakeup_clock(u);
-
-            if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
-                /* Our stream has been suspended so we just flush it... */
-                pa_rtpoll_set_timer_disabled(u->rtpoll);
-                pa_raop_client_flush(u->raop);
-            }
-
-            return 0;
-        }
-
-        case SINK_MESSAGE_UDP_DISCONNECTED: {
-            if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
-                pa_rtpoll_set_timer_disabled(u->rtpoll);
-                if (u->rtpoll_item)
-                    pa_rtpoll_item_free(u->rtpoll_item);
-                u->rtpoll_item = NULL;
-            } else {
-                /* Question: is this valid here: or should we do some sort of:
-                 * return pa_sink_process_msg(PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL); ?? */
-                pa_module_unload_request(u->module, true);
-            }
-
-            pa_close(u->udp_control_fd);
-            pa_close(u->udp_timing_fd);
-
-            u->udp_control_fd = -1;
-            u->udp_timing_fd = -1;
-
-            return 0;
-        }
-    }
-
-    return pa_sink_process_msg(o, code, data, offset, chunk);
-}
-
-static void sink_set_volume_cb(pa_sink *s) {
-    struct userdata *u = s->userdata;
-    pa_cvolume hw;
-    pa_volume_t v, v_orig;
-    char t[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
-
-    pa_assert(u);
-
-    /* If we're muted we don't need to do anything. */
-    if (s->muted)
-        return;
-
-    /* Calculate the max volume of all channels.
-     * We'll use this as our (single) volume on the APEX device and emulate
-     * any variation in channel volumes in software. */
-    v = pa_cvolume_max(&s->real_volume);
-
-    v_orig = v;
-    v = pa_raop_client_adjust_volume(u->raop, v_orig);
-
-    pa_log_debug("Volume adjusted: orig=%u adjusted=%u", v_orig, v);
-
-    /* Create a pa_cvolume version of our single value. */
-    pa_cvolume_set(&hw, s->sample_spec.channels, v);
-
-    /* Set the real volume based on given original volume. */
-    pa_cvolume_set(&s->real_volume, s->sample_spec.channels, v_orig);
-
-    pa_log_debug("Requested volume: %s", pa_cvolume_snprint_verbose(t, sizeof(t), &s->real_volume, &s->channel_map, false));
-    pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint_verbose(t, sizeof(t), &hw, &s->channel_map, false));
-    pa_log_debug("Calculated software volume: %s",
-                 pa_cvolume_snprint_verbose(t, sizeof(t), &s->soft_volume, &s->channel_map, true));
-
-    /* Any necessary software volume manipulation is done so set
-     * our hw volume (or v as a single value) on the device. */
-    pa_raop_client_set_volume(u->raop, v);
-}
-
-static void sink_set_mute_cb(pa_sink *s) {
-    struct userdata *u = s->userdata;
-
-    pa_assert(u);
-
-    if (s->muted) {
-        pa_raop_client_set_volume(u->raop, PA_VOLUME_MUTED);
-    } else {
-        sink_set_volume_cb(s);
-    }
-}
-
-static void udp_setup_cb(int control_fd, int timing_fd, void *userdata) {
-    struct userdata *u = userdata;
-
-    pa_assert(control_fd);
-    pa_assert(timing_fd);
-    pa_assert(u);
-
-    u->udp_control_fd = control_fd;
-    u->udp_timing_fd = timing_fd;
-
-    pa_log_debug("Connection authenticated, syncing with server...");
-
-    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UDP_SETUP, NULL, 0, NULL, NULL);
-}
-
-static void udp_record_cb(void *userdata) {
-    struct userdata *u = userdata;
-
-    pa_assert(u);
-
-    /* Set the initial volume. */
-    sink_set_volume_cb(u->sink);
-
-    pa_log_debug("Synchronization done, pushing job to IO thread...");
-
-    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UDP_RECORD, NULL, 0, NULL, NULL);
-}
-
-static void udp_disconnected_cb(void *userdata) {
-    struct userdata *u = userdata;
-
-    pa_assert(u);
-
-    /* This callback function is called from both STATE_TEARDOWN and
-       STATE_DISCONNECTED in raop_client.c */
-
-    pa_assert(u);
-
-    pa_log_debug("Connection closed, informing IO thread...");
-
-    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UDP_DISCONNECTED, NULL, 0, NULL, NULL);
-}
-
-static void tcp_thread_func(struct userdata *u) {
-    int write_type = 0;
-    pa_memchunk silence;
-    uint32_t silence_overhead = 0;
-    double silence_ratio = 0;
-
-    pa_assert(u);
-
-    pa_log_debug("TCP thread starting up");
-
-    pa_thread_mq_install(&u->thread_mq);
-
-    pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
-
-    /* Create a chunk of memory that is our encoded silence sample. */
-    pa_memchunk_reset(&silence);
-
-    for (;;) {
-        int ret;
-
-        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
-            pa_sink_process_rewind(u->sink, 0);
-
-        if (u->rtpoll_item) {
-            struct pollfd *pollfd;
-            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
-
-            /* Render some data and write it to the fifo. */
-            if (/*PA_SINK_IS_OPENED(u->sink->thread_info.state) && */pollfd->revents) {
-                pa_usec_t usec;
-                int64_t n;
-                void *p;
-
-                if (!silence.memblock) {
-                    pa_memchunk silence_tmp;
-
-                    pa_memchunk_reset(&silence_tmp);
-                    silence_tmp.memblock = pa_memblock_new(u->core->mempool, 4096);
-                    silence_tmp.length = 4096;
-                    p = pa_memblock_acquire(silence_tmp.memblock);
-                      memset(p, 0, 4096);
-                    pa_memblock_release(silence_tmp.memblock);
-                    pa_raop_client_encode_sample(u->raop, &silence_tmp, &silence);
-                    pa_assert(0 == silence_tmp.length);
-                    silence_overhead = silence_tmp.length - 4096;
-                    silence_ratio = silence_tmp.length / 4096;
-                    pa_memblock_unref(silence_tmp.memblock);
-                }
-
-                for (;;) {
-                    ssize_t l;
-
-                    if (u->encoded_memchunk.length <= 0) {
-                        if (u->encoded_memchunk.memblock)
-                            pa_memblock_unref(u->encoded_memchunk.memblock);
-                        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
-                            size_t rl;
-
-                            /* We render real data. */
-                            if (u->raw_memchunk.length <= 0) {
-                                if (u->raw_memchunk.memblock)
-                                    pa_memblock_unref(u->raw_memchunk.memblock);
-                                pa_memchunk_reset(&u->raw_memchunk);
-
-                                /* Grab unencoded data. */
-                                pa_sink_render(u->sink, u->block_size, &u->raw_memchunk);
-                            }
-                            pa_assert(u->raw_memchunk.length > 0);
-
-                            /* Encode it. */
-                            rl = u->raw_memchunk.length;
-                            u->encoding_overhead += u->next_encoding_overhead;
-                            pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk);
-                            u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length));
-                            u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length);
-                        } else {
-                            /* We render some silence into our memchunk. */
-                            memcpy(&u->encoded_memchunk, &silence, sizeof(pa_memchunk));
-                            pa_memblock_ref(silence.memblock);
-
-                            /* Calculate/store some values to be used with the smoother. */
-                            u->next_encoding_overhead = silence_overhead;
-                            u->encoding_ratio = silence_ratio;
-                        }
-                    }
-                    pa_assert(u->encoded_memchunk.length > 0);
-
-                    p = pa_memblock_acquire(u->encoded_memchunk.memblock);
-                    l = pa_write(u->tcp_fd, (uint8_t*) p + u->encoded_memchunk.index, u->encoded_memchunk.length, &write_type);
-                    pa_memblock_release(u->encoded_memchunk.memblock);
-
-                    pa_assert(l != 0);
-
-                    if (l < 0) {
-
-                        if (errno == EINTR)
-                            continue;
-                        else if (errno == EAGAIN) {
-
-                            /* OK, we filled all socket buffers up now. */
-                            goto filled_up;
-
-                        } else {
-                            pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
-                            goto fail;
-                        }
-
-                    } else {
-                        u->offset += l;
-
-                        u->encoded_memchunk.index += l;
-                        u->encoded_memchunk.length -= l;
-
-                        pollfd->revents = 0;
-
-                        if (u->encoded_memchunk.length > 0) {
-                            /* We've completely written the encoded data, so update our overhead. */
-                            u->encoding_overhead += u->next_encoding_overhead;
-
-                            /* OK, we wrote less that we asked for,
-                             * hence we can assume that the socket
-                             * buffers are full now. */
-                            goto filled_up;
-                        }
-                    }
-                }
-
-            filled_up:
-                /* At this spot we know that the socket buffers are
-                 * fully filled up. This is the best time to estimate
-                 * the playback position of the server. */
-
-                n = u->offset - u->encoding_overhead;
-
-#ifdef SIOCOUTQ
-                {
-                    int l;
-                    if (ioctl(u->tcp_fd, SIOCOUTQ, &l) >= 0 && l > 0)
-                        n -= (l / u->encoding_ratio);
-                }
-#endif
-
-                usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
-
-                if (usec > u->latency)
-                    usec -= u->latency;
-                else
-                    usec = 0;
-
-                pa_smoother_put(u->smoother, pa_rtclock_now(), usec);
-            }
-
-            /* Hmm, nothing to do. Let's sleep... */
-            pollfd->events = POLLOUT; /*PA_SINK_IS_OPENED(u->sink->thread_info.state)  ? POLLOUT : 0;*/
-        }
-
-        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
-            goto fail;
-
-        if (ret == 0)
-            goto finish;
-
-        if (u->rtpoll_item) {
-            struct pollfd* pollfd;
-
-            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
-
-            if (pollfd->revents & ~POLLOUT) {
-                if (u->sink->thread_info.state != PA_SINK_SUSPENDED) {
-                    pa_log("FIFO shutdown.");
-                    goto fail;
-                }
-
-                /* We expect this to happen on occasion if we are not sending data.
-                 * It's perfectly natural and normal and natural. */
-                if (u->rtpoll_item)
-                    pa_rtpoll_item_free(u->rtpoll_item);
-                u->rtpoll_item = NULL;
-            }
-        }
-    }
-
-fail:
-    /* If this was no regular exit from the loop we have to continue
-     * processing messages until we received PA_MESSAGE_SHUTDOWN. */
-    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
-    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
-
-finish:
-    if (silence.memblock)
-        pa_memblock_unref(silence.memblock);
-    pa_log_debug("TCP thread shutting down");
-}
-
-static void udp_thread_func(struct userdata *u) {
-    pa_assert(u);
-
-    pa_log_debug("UDP thread starting up");
-
-    pa_thread_mq_install(&u->thread_mq);
-    pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
-
-    for (;;) {
-        pa_usec_t estimated;
-        int32_t overhead = 0;
-        ssize_t written = 0;
-        size_t length = 0;
-        int rv = 0;
-
-        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
-            if (u->sink->thread_info.rewind_requested)
-                pa_sink_process_rewind(u->sink, 0);
-        }
-
-        /* Polling (audio data + control socket + timing socket). */
-        if ((rv = pa_rtpoll_run(u->rtpoll)) < 0)
-            goto fail;
-        else if (rv == 0)
-            goto finish;
-
-        if (!pa_rtpoll_timer_elapsed(u->rtpoll)) {
-            struct pollfd *pollfd;
-            uint8_t packet[32];
-            ssize_t read;
-
-            if (u->rtpoll_item) {
-                pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
-
-                /* Event on the control socket ?? */
-                if (pollfd->revents & POLLIN) {
-                    pollfd->revents = 0;
-                    pa_log_debug("Received control packet.");
-                    read = pa_read(pollfd->fd, packet, sizeof(packet), NULL);
-                    pa_raop_client_udp_handle_control_packet(u->raop, packet, read);
-                }
-
-                pollfd++;
-
-                /* Event on the timing port ?? */
-                if (pollfd->revents & POLLIN) {
-                    pollfd->revents = 0;
-                    pa_log_debug("Received timing packet.");
-                    read = pa_read(pollfd->fd, packet, sizeof(packet), NULL);
-                    pa_raop_client_udp_handle_timing_packet(u->raop, packet, read);
-                }
-            }
-
-            continue;
-        }
-
-        if (u->sink->thread_info.state != PA_SINK_RUNNING)
-            continue;
-        if (!pa_raop_client_udp_can_stream(u->raop))
-            continue;
-
-        if (u->encoded_memchunk.length <= 0) {
-            if (u->encoded_memchunk.memblock != NULL)
-                pa_memblock_unref(u->encoded_memchunk.memblock);
-
-            if (u->raw_memchunk.length <= 0) {
-                if (u->raw_memchunk.memblock)
-                    pa_memblock_unref(u->raw_memchunk.memblock);
-                pa_memchunk_reset(&u->raw_memchunk);
-
-                /* Grab unencoded audio data from PulseAudio. */
-                pa_sink_render_full(u->sink, u->block_size, &u->raw_memchunk);
-            }
-
-            pa_assert(u->raw_memchunk.length > 0);
-
-            length = u->raw_memchunk.length;
-            pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk);
-            u->encoding_ratio = (double) u->encoded_memchunk.length / (double) (length - u->raw_memchunk.length);
-            overhead = u->encoded_memchunk.length - (length - u->raw_memchunk.length);
-        }
-
-        pa_assert(u->encoded_memchunk.length > 0);
-
-        written = pa_raop_client_udp_send_audio_packet(u->raop,&u->encoded_memchunk);
-        if (written < 0) {
-            pa_log("Failed to send UDP packet: %s", pa_cstrerror(errno));
-            goto fail;
-        }
-
-        u->udp_sent_packets++;
-        /* Sleep until next packet transmission */
-        pa_rtpoll_set_timer_absolute(u->rtpoll, udp_next_wakeup_clock(u));
-
-        u->offset += written;
-        u->encoding_overhead += overhead;
-
-        estimated = pa_bytes_to_usec(u->offset - u->encoding_overhead, &u->sink->sample_spec);
-        pa_smoother_put(u->smoother, pa_rtclock_now(), estimated);
-    }
-
-fail:
-    /* If this was no regular exit, continue processing messages until PA_MESSAGE_SHUTDOWN. */
-    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
-    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
-
-finish:
-    pa_log_debug("UDP thread shutting down");
-}
-
-static void thread_func(void *userdata) {
-    struct userdata *u = userdata;
-
-    if (u->protocol == RAOP_TCP)
-        tcp_thread_func(u);
-    else if (u->protocol == RAOP_UDP)
-        udp_thread_func(u);
-    else
-        pa_assert(false);
-
-    return;
-}
-
 int pa__init(pa_module *m) {
-    struct userdata *u = NULL;
-    pa_sample_spec ss;
     pa_modargs *ma = NULL;
-    const char *server, *protocol, *encryption;
-    pa_sink_new_data data;
-    char *t = NULL;
 
     pa_assert(m);
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log("failed to parse module arguments");
-        goto fail;
-    }
-
-    ss.format = PA_SAMPLE_S16NE;
-    ss.channels = 2;
-    ss.rate = m->core->default_sample_spec.rate;
-    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
-        pa_log("invalid sample format specification");
-        goto fail;
-    }
-
-    if ((ss.format != PA_SAMPLE_S16NE) ||
-        (ss.channels > 2)) {
-        pa_log("sample type support is limited to mono/stereo and S16NE sample data");
-        goto fail;
-    }
-
-    u = pa_xnew0(struct userdata, 1);
-    u->core = m->core;
-    u->module = m;
-    m->userdata = u;
-    u->tcp_fd = -1;
-    u->smoother = pa_smoother_new(
-            PA_USEC_PER_SEC,
-            PA_USEC_PER_SEC*2,
-            true,
-            true,
-            10,
-            0,
-            false);
-    pa_memchunk_reset(&u->raw_memchunk);
-    pa_memchunk_reset(&u->encoded_memchunk);
-    u->offset = 0;
-    u->encoding_overhead = 0;
-    u->next_encoding_overhead = 0;
-    u->encoding_ratio = 1.0;
-
-    u->rtpoll = pa_rtpoll_new();
-
-    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
-        pa_log("pa_thread_mq_init() failed.");
-        goto fail;
-    }
-
-    u->rtpoll_item = NULL;
-
-    /*u->format =
-        (ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) |
-        (ss.channels == 2 ? ESD_STEREO : ESD_MONO);*/
-    u->rate = ss.rate;
-    u->block_size = pa_usec_to_bytes(PA_USEC_PER_SEC/20, &ss);
-
-    u->read_data = u->write_data = NULL;
-    u->read_index = u->write_index = u->read_length = u->write_length = 0;
-
-    /*u->state = STATE_AUTH;*/
-    u->latency = 0;
-
-    if (!(server = pa_modargs_get_value(ma, "server", NULL))) {
-        pa_log("No server argument given.");
-        goto fail;
-    }
-
-    /* This may be overwriten if sink_name is specified in module arguments. */
-    t = pa_sprintf_malloc("raop_client.%s", server);
-
-    protocol = pa_modargs_get_value(ma, "protocol", NULL);
-    if (protocol == NULL || pa_streq(protocol, "TCP")) {
-        /* Assume TCP by default */
-        u->protocol = RAOP_TCP;
-    }
-    else if (pa_streq(protocol, "UDP")) {
-        u->protocol = RAOP_UDP;
-    } else {
-        pa_log("Unsupported protocol argument given: %s", protocol);
-        goto fail;
-    }
-
-    pa_sink_new_data_init(&data);
-    data.driver = __FILE__;
-    data.module = m;
-    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", t));
-    pa_sink_new_data_set_sample_spec(&data, &ss);
-    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server);
-    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "music");
-    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "RAOP sink '%s'", server);
-
-    /* RAOP discover module will eventually overwrite sink_name and others
-       (PA_UPDATE_REPLACE). */
-    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
-        pa_log("Invalid properties");
-        pa_sink_new_data_done(&data);
-        goto fail;
-    }
-
-    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_NETWORK);
-    pa_xfree(t); t = NULL;
-    pa_sink_new_data_done(&data);
-
-    if (!u->sink) {
-        pa_log("Failed to create sink.");
-        goto fail;
-    }
-
-    if (u->protocol == RAOP_TCP)
-        u->sink->parent.process_msg = tcp_sink_process_msg;
-    else
-        u->sink->parent.process_msg = udp_sink_process_msg;
-    u->sink->userdata = u;
-    pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
-    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
-    u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK;
-
-    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
-    pa_sink_set_rtpoll(u->sink, u->rtpoll);
-
-    if (!(u->raop = pa_raop_client_new(u->core, server, u->protocol))) {
-        pa_log("Failed to connect to server.");
+        pa_log("Failed to parse module arguments");
         goto fail;
     }
 
-    encryption = pa_modargs_get_value(ma, "encryption", NULL);
-    pa_raop_client_set_encryption(u->raop, !pa_safe_streq(encryption, "none"));
-
-    pa_raop_client_tcp_set_callback(u->raop, tcp_on_connection, u);
-    pa_raop_client_tcp_set_closed_callback(u->raop, tcp_on_close, u);
-
-    if (u->protocol == RAOP_UDP) {
-        /* The number of frames per blocks is not negotiable... */
-        pa_raop_client_udp_get_blocks_size(u->raop, &u->block_size);
-        u->block_size *= pa_frame_size(&ss);
-        pa_sink_set_max_request(u->sink, u->block_size);
-
-        pa_raop_client_udp_set_setup_callback(u->raop, udp_setup_cb, u);
-        pa_raop_client_udp_set_record_callback(u->raop, udp_record_cb, u);
-        pa_raop_client_udp_set_disconnected_callback(u->raop, udp_disconnected_cb, u);
-    }
-
-    if (!(u->thread = pa_thread_new("raop-sink", thread_func, u))) {
-        pa_log("Failed to create thread.");
+    if (!(m->userdata = pa_raop_sink_new(m, ma, __FILE__)))
         goto fail;
-    }
-
-    pa_sink_put(u->sink);
 
     pa_modargs_free(ma);
 
     return 0;
 
 fail:
-    pa_xfree(t);
 
     if (ma)
         pa_modargs_free(ma);
@@ -1000,57 +92,19 @@ fail:
 }
 
 int pa__get_n_used(pa_module *m) {
-    struct userdata *u;
+    pa_sink *sink;
 
     pa_assert(m);
-    pa_assert_se(u = m->userdata);
+    pa_assert_se(sink = m->userdata);
 
-    return pa_sink_linked_by(u->sink);
+    return pa_sink_linked_by(sink);
 }
 
 void pa__done(pa_module *m) {
-    struct userdata *u;
-    pa_assert(m);
-
-    if (!(u = m->userdata))
-        return;
-
-    if (u->sink)
-        pa_sink_unlink(u->sink);
-
-    if (u->thread) {
-        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
-        pa_thread_free(u->thread);
-    }
-
-    pa_thread_mq_done(&u->thread_mq);
-
-    if (u->sink)
-        pa_sink_unref(u->sink);
-
-    if (u->rtpoll_item)
-        pa_rtpoll_item_free(u->rtpoll_item);
-
-    if (u->rtpoll)
-        pa_rtpoll_free(u->rtpoll);
-
-    if (u->raw_memchunk.memblock)
-        pa_memblock_unref(u->raw_memchunk.memblock);
-
-    if (u->encoded_memchunk.memblock)
-        pa_memblock_unref(u->encoded_memchunk.memblock);
-
-    if (u->raop)
-        pa_raop_client_free(u->raop);
-
-    pa_xfree(u->read_data);
-    pa_xfree(u->write_data);
-
-    if (u->smoother)
-        pa_smoother_free(u->smoother);
+    pa_sink *sink;
 
-    if (u->tcp_fd >= 0)
-        pa_close(u->tcp_fd);
+    pa_assert(m);
 
-    pa_xfree(u);
+    if ((sink = m->userdata))
+        pa_raop_sink_free(sink);
 }
diff --git a/src/modules/raop/raop-client.c b/src/modules/raop/raop-client.c
new file mode 100644
index 0000000..813b161
--- /dev/null
+++ b/src/modules/raop/raop-client.c
@@ -0,0 +1,1732 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio 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 Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <math.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/sample.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/arpa-inet.h>
+#include <pulsecore/socket-client.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/parseaddr.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/random.h>
+#include <pulsecore/poll.h>
+
+#include "raop-client.h"
+#include "raop-packet-buffer.h"
+#include "raop-crypto.h"
+#include "raop-util.h"
+
+#include "rtsp_client.h"
+
+#define DEFAULT_RAOP_PORT 5000
+
+#define FRAMES_PER_TCP_PACKET 4096
+#define FRAMES_PER_UDP_PACKET 352
+
+#define DEFAULT_TCP_AUDIO_PORT   6000
+#define DEFAULT_UDP_AUDIO_PORT   6000
+#define DEFAULT_UDP_CONTROL_PORT 6001
+#define DEFAULT_UDP_TIMING_PORT  6002
+
+#define DEFAULT_USER_AGENT "iTunes/11.0.4 (Windows; N)"
+#define DEFAULT_USER_NAME  "iTunes"
+
+#define JACK_STATUS_DISCONNECTED 0
+#define JACK_STATUS_CONNECTED    1
+#define JACK_TYPE_ANALOG         0
+#define JACK_TYPE_DIGITAL        1
+
+#define VOLUME_MAX  0.0
+#define VOLUME_DEF -30.0
+#define VOLUME_MIN -144.0
+
+#define UDP_DEFAULT_PKT_BUF_SIZE 1000
+
+struct pa_raop_client {
+    pa_core *core;
+    char *host;
+    uint16_t port;
+    pa_rtsp_client *rtsp;
+    char *sci, *sid;
+    char *password;
+
+    pa_raop_protocol_t protocol;
+    pa_raop_encryption_t encryption;
+    pa_raop_codec_t codec;
+
+    pa_raop_secret *secret;
+
+    int tcp_sfd;
+
+    int udp_sfd;
+    int udp_cfd;
+    int udp_tfd;
+
+    pa_raop_packet_buffer *pbuf;
+
+    uint16_t seq;
+    uint32_t rtptime;
+    bool is_recording;
+    uint32_t ssrc;
+
+    bool is_first_packet;
+    uint32_t sync_interval;
+    uint32_t sync_count;
+
+    uint8_t jack_type;
+    uint8_t jack_status;
+
+    pa_raop_client_state_cb_t state_callback;
+    void *state_userdata;
+};
+
+/* Audio TCP packet header [16x8] (cf. rfc4571):
+ *  [0,1]   Frame marker; seems always 0x2400
+ *  [2,3]   RTP packet size (following): 0x0000 (to be set)
+ *   [4,5]   RTP v2: 0x80
+ *   [5]     Payload type: 0x60 | Marker bit: 0x80 (always set)
+ *   [6,7]   Sequence number: 0x0000 (to be set)
+ *   [8,11]  Timestamp: 0x00000000 (to be set)
+ *   [12,15] SSRC: 0x00000000 (to be set) */
+#define PAYLOAD_TCP_AUDIO_DATA 0x60
+static const uint8_t tcp_audio_header[16] = {
+    0x24, 0x00, 0x00, 0x00,
+    0x80, 0xe0, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00
+};
+
+/* Audio UDP packet header [12x8] (cf. rfc3550):
+ *  [0]    RTP v2: 0x80
+ *  [1]    Payload type: 0x60
+ *  [2,3]  Sequence number: 0x0000 (to be set)
+ *  [4,7]  Timestamp: 0x00000000 (to be set)
+ *  [8,12] SSRC: 0x00000000 (to be set) */
+#define PAYLOAD_UDP_AUDIO_DATA 0x60
+static const uint8_t udp_audio_header[12] = {
+    0x80, 0x60, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00
+};
+
+/* Audio retransmission UDP packet header [4x8]:
+ *  [0] RTP v2: 0x80
+ *  [1] Payload type: 0x56 | Marker bit: 0x80 (always set)
+ *  [2] Unknown; seems always 0x01
+ *  [3] Unknown; seems some random number around 0x20~0x40 */
+#define PAYLOAD_RETRANSMIT_REQUEST 0x55
+#define PAYLOAD_RETRANSMIT_REPLY   0x56
+static const uint8_t udp_audio_retrans_header[4] = {
+    0x80, 0xd6, 0x00, 0x00
+};
+
+/* Sync packet header [8x8] (cf. rfc3550):
+ *  [0]   RTP v2: 0x80
+ *  [1]   Payload type: 0x54 | Marker bit: 0x80 (always set)
+ *  [2,3] Sequence number: 0x0007
+ *  [4,7] Timestamp: 0x00000000 (to be set) */
+static const uint8_t udp_sync_header[8] = {
+    0x80, 0xd4, 0x00, 0x07,
+    0x00, 0x00, 0x00, 0x00
+};
+
+/* Timming packet header [8x8] (cf. rfc3550):
+ *  [0]   RTP v2: 0x80
+ *  [1]   Payload type: 0x53 | Marker bit: 0x80 (always set)
+ *  [2,3] Sequence number: 0x0007
+ *  [4,7] Timestamp: 0x00000000 (unused) */
+#define PAYLOAD_TIMING_REQUEST  0x52
+#define PAYLOAD_TIMING_REPLY    0x53
+static const uint8_t udp_timming_header[8] = {
+    0x80, 0xd3, 0x00, 0x07,
+    0x00, 0x00, 0x00, 0x00
+};
+
+/**
+ * Function to trim a given character at the end of a string (no realloc).
+ * @param str Pointer to string
+ * @param rc Character to trim
+ */
+static inline void rtrim_char(char *str, char rc) {
+    char *sp = str + strlen(str) - 1;
+    while (sp >= str && *sp == rc) {
+        *sp = '\0';
+        sp -= 1;
+    }
+}
+
+/**
+ * Function to convert a timeval to ntp timestamp.
+ * @param tv Pointer to the timeval structure
+ * @return The NTP timestamp
+ */
+static inline uint64_t timeval_to_ntp(struct timeval *tv) {
+    uint64_t ntp = 0;
+
+    /* Converting micro seconds to a fraction. */
+    ntp = (uint64_t) tv->tv_usec * UINT32_MAX / PA_USEC_PER_SEC;
+    /* Moving reference from  1 Jan 1970 to 1 Jan 1900 (seconds). */
+    ntp |= (uint64_t) (tv->tv_sec + 0x83aa7e80) << 32;
+
+    return ntp;
+}
+
+/**
+ * Function to write bits into a buffer.
+ * @param buffer Handle to the buffer. It will be incremented if new data requires it.
+ * @param bit_pos A pointer to a position buffer to keep track the current write location (0 for MSB, 7 for LSB)
+ * @param size A pointer to the byte size currently written. This allows the calling function to do simple buffer overflow checks
+ * @param data The data to write
+ * @param data_bit_len The number of bits from data to write
+ */
+static inline void bit_writer(uint8_t **buffer, uint8_t *bit_pos, size_t *size, uint8_t data, uint8_t data_bit_len) {
+    int bits_left, bit_overflow;
+    uint8_t bit_data;
+
+    if (!data_bit_len)
+        return;
+
+    /* If bit pos is zero, we will definately use at least one bit from the current byte so size increments. */
+    if (!*bit_pos)
+        *size += 1;
+
+    /* Calc the number of bits left in the current byte of buffer. */
+    bits_left = 7 - *bit_pos  + 1;
+    /* Calc the overflow of bits in relation to how much space we have left... */
+    bit_overflow = bits_left - data_bit_len;
+    if (bit_overflow >= 0) {
+        /* We can fit the new data in our current byte.
+         * As we write from MSB->LSB we need to left shift by the overflow amount. */
+        bit_data = data << bit_overflow;
+        if (*bit_pos)
+            **buffer |= bit_data;
+        else
+            **buffer = bit_data;
+        /* If our data fits exactly into the current byte, we need to increment our pointer. */
+        if (0 == bit_overflow) {
+            /* Do not increment size as it will be incremented on next call as bit_pos is zero. */
+            *buffer += 1;
+            *bit_pos = 0;
+        } else {
+            *bit_pos += data_bit_len;
+        }
+    } else {
+        /* bit_overflow is negative, there for we will need a new byte from our buffer
+         * Firstly fill up what's left in the current byte. */
+        bit_data = data >> -bit_overflow;
+        **buffer |= bit_data;
+        /* Increment our buffer pointer and size counter. */
+        *buffer += 1;
+        *size += 1;
+        **buffer = data << (8 + bit_overflow);
+        *bit_pos = -bit_overflow;
+    }
+}
+
+static size_t write_PCM_data(uint8_t *packet, const size_t max, uint8_t *raw, size_t *length) {
+    size_t size = 0;
+
+    pa_memzero(packet, max);
+
+    pa_log("Raw PCM not implemented...");
+
+    return size;
+}
+
+static size_t write_ALAC_data(uint8_t *packet, const size_t max, uint8_t *raw, size_t *length, bool compress) {
+    uint32_t nbs = (*length / 2) / 2;
+    uint8_t *ibp, *maxibp;
+    uint8_t *bp, bpos;
+    size_t size = 0;
+
+    bp = packet;
+    pa_memzero(packet, max);
+    size = bpos = 0;
+
+    bit_writer(&bp, &bpos, &size, 1, 3); /* channel=1, stereo */
+    bit_writer(&bp, &bpos, &size, 0, 4); /* Unknown */
+    bit_writer(&bp, &bpos, &size, 0, 8); /* Unknown */
+    bit_writer(&bp, &bpos, &size, 0, 4); /* Unknown */
+    bit_writer(&bp, &bpos, &size, 1, 1); /* Hassize */
+    bit_writer(&bp, &bpos, &size, 0, 2); /* Unused */
+    bit_writer(&bp, &bpos, &size, 1, 1); /* Is-not-compressed */
+    /* Size of data, integer, big endian. */
+    bit_writer(&bp, &bpos, &size, (nbs >> 24) & 0xff, 8);
+    bit_writer(&bp, &bpos, &size, (nbs >> 16) & 0xff, 8);
+    bit_writer(&bp, &bpos, &size, (nbs >> 8)  & 0xff, 8);
+    bit_writer(&bp, &bpos, &size, (nbs)       & 0xff, 8);
+
+    ibp = raw;
+    maxibp = raw + (4 * nbs) - 4;
+    while (ibp <= maxibp) {
+        /* Byte swap stereo data. */
+        bit_writer(&bp, &bpos, &size, *(ibp + 1), 8);
+        bit_writer(&bp, &bpos, &size, *(ibp + 0), 8);
+        bit_writer(&bp, &bpos, &size, *(ibp + 3), 8);
+        bit_writer(&bp, &bpos, &size, *(ibp + 2), 8);
+        ibp += 4;
+    }
+
+    *length = (ibp - raw);
+    return size;
+}
+
+static size_t write_AAC_data(uint8_t *packet, const size_t max, uint8_t *raw, size_t *length) {
+    size_t size = 0;
+
+    pa_memzero(packet, max);
+
+    pa_log("AAC encoding not implemented...");
+
+    return size;
+}
+
+static size_t build_tcp_audio_packet(pa_raop_client *c, uint8_t *raw, size_t *index, size_t *length, uint32_t **packet) {
+    const size_t max = sizeof(tcp_audio_header) + 8 + 16384;
+    uint32_t *buffer = NULL;
+    size_t size, head;
+
+    *packet = NULL;
+    if (!(buffer = pa_xmalloc0(max)))
+        return 0;
+
+    size = head = sizeof(tcp_audio_header);
+    memcpy(buffer, tcp_audio_header, sizeof(tcp_audio_header));
+    buffer[1] |= htonl((uint32_t) c->seq);
+    buffer[2] = htonl(c->rtptime);
+    buffer[3] = htonl(c->ssrc);
+
+    if (c->codec == PA_RAOP_CODEC_PCM)
+        size += write_PCM_data(((uint8_t *) buffer + head), max - head, raw, length);
+    else if (c->codec == PA_RAOP_CODEC_ALAC)
+        size += write_ALAC_data(((uint8_t *) buffer + head), max - head, raw, length, false);
+    else
+        size += write_AAC_data(((uint8_t *) buffer + head), max - head, raw, length);
+    c->rtptime += *length / 4;
+
+    buffer[0] |= htonl((uint32_t) size - 4);
+    if (c->encryption == PA_RAOP_ENCRYPTION_RSA)
+        pa_raop_aes_encrypt(c->secret, (uint8_t *) buffer + head, size - head);
+
+    *packet = buffer;
+    return size;
+}
+
+static ssize_t send_tcp_audio_packet(pa_raop_client *c, pa_memchunk *block, size_t offset) {
+    static uint32_t * packet = NULL;
+    static size_t size, sent;
+    double progress = 0.0;
+    size_t index, length;
+    uint8_t *raw = NULL;
+    ssize_t written;
+
+    if (!packet) {
+        index = block->index;
+        length = block->length;
+        raw = pa_memblock_acquire(block->memblock);
+
+        pa_assert(raw);
+        pa_assert(index == offset);
+        pa_assert(length > 0);
+
+        size = build_tcp_audio_packet(c, raw, &index, &length, &packet);
+        sent = 0;
+    }
+
+    written = -1;
+    if (packet != NULL && size > 0)
+        written = pa_write(c->tcp_sfd, packet + sent, size - sent, NULL);
+    if (block->index == offset)
+        c->seq++;
+    if (sent == 0)
+        pa_memblock_release(block->memblock);
+    if (written > 0) {
+        sent += written;
+        progress = (double) sent / (double) size;
+        index = (block->index + block->length) * progress;
+        length = (block->index + block->length) - index;
+        block->length = length;
+        block->index = index;
+    }
+
+    if ((size - sent) <= 0) {
+        pa_xfree(packet);
+        packet = NULL;
+    }
+
+    return written;
+}
+
+static size_t build_udp_audio_packet(pa_raop_client *c, uint8_t *raw, size_t *index, size_t *length, uint32_t **packet) {
+    const size_t max = sizeof(udp_audio_header) + 8 + 1408;
+    uint32_t *buffer = NULL;
+    size_t size, head;
+
+    *packet = NULL;
+    if (!(buffer = pa_xmalloc0(max)))
+        return 0;
+
+    size = head = sizeof(udp_audio_header);
+    memcpy(buffer, udp_audio_header, sizeof(udp_audio_header));
+    if (c->is_first_packet)
+        buffer[0] |= htonl((uint32_t) 0x80 << 16);
+    buffer[0] |= htonl((uint32_t) c->seq);
+    buffer[1] = htonl(c->rtptime);
+    buffer[2] = htonl(c->ssrc);
+
+    if (c->codec == PA_RAOP_CODEC_PCM)
+        size += write_PCM_data(((uint8_t *) buffer + head), max - head, raw + *index, length);
+    else if (c->codec == PA_RAOP_CODEC_ALAC)
+        size += write_ALAC_data(((uint8_t *) buffer + head), max - head, raw + *index, length, false);
+    else
+        size += write_AAC_data(((uint8_t *) buffer + head), max - head, raw + *index, length);
+    c->rtptime += *length / 4;
+
+    if (c->encryption == PA_RAOP_ENCRYPTION_RSA)
+        pa_raop_aes_encrypt(c->secret, (uint8_t *) buffer + head, size - head);
+
+    *index += *length;
+    *length = 0;
+    /* It is meaningless to preseve the partial data -> */
+    *packet = buffer;
+    return size;
+}
+
+static ssize_t send_udp_audio_packet(pa_raop_client *c, pa_memchunk *block, size_t offset) {
+    uint32_t *packet = NULL;
+    size_t index, length, size;
+    uint8_t *raw = NULL;
+    ssize_t written;
+
+    index = block->index;
+    length = block->length;
+    raw = pa_memblock_acquire(block->memblock);
+
+    pa_assert(raw);
+    /* <- UDP packet has to be sent at once ! */
+    pa_assert(index == offset);
+    pa_assert(length > 0);
+
+    written = -1;
+    size = build_udp_audio_packet(c, raw, &index, &length, &packet);
+    if (packet != NULL && size > 0)
+        written = pa_write(c->udp_sfd, packet, size, NULL);
+    if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
+        pa_log_debug("Discarding UDP (audio, seq=%d) packet due to EAGAIN (%s)", c->seq, pa_cstrerror(errno));
+    c->seq++;
+
+    /* Store packet for resending in the packet buffer (UDP). */
+    pa_raop_pb_write_packet(c->pbuf, c->seq, raw + block->index, block->length);
+    pa_xfree(packet);
+
+    pa_memblock_release(block->memblock);
+    block->length = length;
+    block->index = index;
+
+    if (written < 0)
+        return (ssize_t) size;
+    return written;
+}
+
+static size_t rebuild_udp_audio_packet(pa_raop_client *c, uint16_t seq, uint32_t **packet) {
+    size_t size = sizeof(udp_audio_retrans_header);
+    uint32_t *buffer = NULL;
+    uint8_t *data = NULL;
+
+    size += pa_raop_pb_read_packet(c->pbuf, seq, &data);
+    if (size == sizeof(udp_audio_retrans_header))
+        return 0;
+    if (!(buffer = pa_xmalloc0(size)))
+        return 0;
+
+    memcpy(buffer, udp_audio_retrans_header, sizeof(udp_audio_retrans_header));
+    buffer[0] |= htonl((uint32_t) seq);
+
+    *packet = buffer;
+    return size;
+}
+
+static ssize_t resend_udp_audio_packets(pa_raop_client *c, uint16_t seq, uint16_t nbp) {
+    ssize_t total = 0;
+    int i = 0;
+
+    for (i = 0; i < nbp; i++) {
+        uint32_t * packet = NULL;
+        ssize_t written = 0;
+        size_t size = 0;
+
+        size = rebuild_udp_audio_packet(c, seq, &packet);
+        if (packet != NULL && size > 0)
+            written = pa_write(c->udp_cfd, packet, size, NULL);
+        if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+            pa_log_debug("Discarding UDP (audio-restransmitted, seq=%d) packet due to EAGAIN", c->seq);
+            continue;
+        }
+
+        if (written > 0)
+            total +=  written;
+    }
+
+    return total;
+}
+
+static size_t build_udp_sync_packet(pa_raop_client *c, uint32_t stamp, uint32_t **packet) {
+    const size_t size = sizeof(udp_sync_header) + 12;
+    const uint32_t delay = 88200;
+    uint32_t *buffer = NULL;
+    uint64_t transmitted = 0;
+    struct timeval tv;
+
+    *packet = NULL;
+    if (!(buffer = pa_xmalloc0(size)))
+        return 0;
+
+    memcpy(buffer, udp_sync_header, sizeof(udp_sync_header));
+    if (c->is_first_packet)
+        buffer[0] |= 0x10;
+    stamp -= delay;
+    buffer[1] = htonl(stamp);
+    /* Set the transmited timestamp to current time. */
+    transmitted = timeval_to_ntp(pa_rtclock_get(&tv));
+    buffer[2] = htonl(transmitted >> 32);
+    buffer[3] = htonl(transmitted & 0xffffffff);
+    stamp += delay;
+    buffer[4] = htonl(stamp);
+
+    *packet = buffer;
+    return size;
+}
+
+static ssize_t send_udp_sync_packet(pa_raop_client *c, uint32_t stamp) {
+    uint32_t * packet = NULL;
+    ssize_t written = 0;
+    size_t size = 0;
+
+    size = build_udp_sync_packet(c, stamp, &packet);
+    if (packet != NULL && size > 0)
+        written = pa_loop_write(c->udp_cfd, packet, size, NULL);
+
+    return written;
+}
+
+static size_t handle_udp_control_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size) {
+    uint8_t payload = 0;
+    uint16_t seq, nbp = 0;
+    ssize_t written = 0;
+
+    /* Control packets are 8 bytes long:  */
+    if (size != 8 || packet[0] != 0x80) {
+        pa_log_debug("Received an invalid control packet...");
+        return 1;
+    }
+
+    seq = ntohs((uint16_t) packet[4]);
+    nbp = ntohs((uint16_t) packet[6]);
+    if (nbp <= 0) {
+        pa_log_debug("Received an invalid control packet...");
+        return 1;
+    }
+
+    /* The market bit is always set (see rfc3550 for packet structure) ! */
+    payload = packet[1] ^ 0x80;
+    switch (payload) {
+        case PAYLOAD_RETRANSMIT_REQUEST:
+            pa_log_debug("Resending %u packets starting at %u", nbp, seq);
+            written = resend_udp_audio_packets(c, seq, nbp);
+            break;
+        case PAYLOAD_RETRANSMIT_REPLY:
+        default:
+            pa_log_debug("Got an unexpected payload type on control channel (%u) !", payload);
+            break;
+    }
+
+    return written;
+}
+
+static size_t build_udp_timing_packet(pa_raop_client *c, const uint32_t data[6], uint64_t received, uint32_t **packet) {
+    const size_t size = sizeof(udp_timming_header) + 24;
+    uint32_t *buffer = NULL;
+    uint64_t transmitted = 0;
+    struct timeval tv;
+
+    *packet = NULL;
+    if (!(buffer = pa_xmalloc0(size)))
+        return 0;
+
+    memcpy(buffer, udp_timming_header, sizeof(udp_timming_header));
+    /* Copying originate timestamp from the incoming request packet. */
+    buffer[2] = data[4];
+    buffer[3] = data[5];
+    /* Set the receive timestamp to reception time. */
+    buffer[4] = htonl(received >> 32);
+    buffer[5] = htonl(received & 0xffffffff);
+    /* Set the transmit timestamp to current time. */
+    transmitted = timeval_to_ntp(pa_rtclock_get(&tv));
+    buffer[6] = htonl(transmitted >> 32);
+    buffer[7] = htonl(transmitted & 0xffffffff);
+
+    *packet = buffer;
+    return size;
+}
+
+static ssize_t send_udp_timing_packet(pa_raop_client *c, const uint32_t data[6], uint64_t received) {
+    uint32_t * packet = NULL;
+    ssize_t written = 0;
+    size_t size = 0;
+
+    size = build_udp_timing_packet(c, data, received, &packet);
+    if (packet != NULL && size > 0)
+        written = pa_loop_write(c->udp_tfd, packet, size, NULL);
+
+    return written;
+}
+
+static size_t handle_udp_timing_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size) {
+    const uint32_t * data = NULL;
+    uint8_t payload = 0;
+    struct timeval tv;
+    size_t written = 0;
+    uint64_t rci = 0;
+
+    /* Timing packets are 32 bytes long: 1 x 8 RTP header (no ssrc) + 3 x 8 NTP timestamps */
+    if (size != 32 || packet[0] != 0x80) {
+        pa_log_debug("Received an invalid UDP timing packet...");
+        return 0;
+    }
+
+    rci = timeval_to_ntp(pa_rtclock_get(&tv));
+    data = (uint32_t *) (packet + sizeof(udp_timming_header));
+
+    /* The market bit is always set (see rfc3550 for packet structure) ! */
+    payload = packet[1] ^ 0x80;
+    switch (payload) {
+        case PAYLOAD_TIMING_REQUEST:
+            pa_log_debug("Sending timing packet at %lu", rci);
+            written = send_udp_timing_packet(c, data, rci);
+            break;
+        case PAYLOAD_TIMING_REPLY:
+        default:
+            pa_log_debug("Got an unexpected payload type on timing channel (%u) !", payload);
+            break;
+    }
+
+    return written;
+}
+
+static int connect_udp_socket(pa_raop_client *c, int fd, uint16_t port) {
+    struct sockaddr_in sa4;
+#ifdef HAVE_IPV6
+    struct sockaddr_in6 sa6;
+#endif
+    struct sockaddr *sa;
+    socklen_t salen;
+    sa_family_t af;
+
+    pa_zero(sa4);
+#ifdef HAVE_IPV6
+    pa_zero(sa6);
+#endif
+    if (inet_pton(AF_INET, c->host, &sa4.sin_addr) > 0) {
+        sa4.sin_family = af = AF_INET;
+        sa4.sin_port = htons(port);
+        sa = (struct sockaddr *) &sa4;
+        salen = sizeof(sa4);
+#ifdef HAVE_IPV6
+    } else if (inet_pton(AF_INET6, c->host, &sa6.sin6_addr) > 0) {
+        sa6.sin6_family = af = AF_INET6;
+        sa6.sin6_port = htons(port);
+        sa = (struct sockaddr *) &sa6;
+        salen = sizeof(sa6);
+#endif
+    } else {
+        pa_log("Invalid destination '%s'", c->host);
+        goto fail;
+    }
+
+    if (fd < 0 && (fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) {
+        pa_log("socket() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    /* If the socket queue is full, let's drop packets */
+    pa_make_udp_socket_low_delay(fd);
+    pa_make_fd_nonblock(fd);
+
+    if (connect(fd, sa, salen) < 0) {
+        pa_log("connect() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_log_debug("Connected to %s on port %d (SOCK_DGRAM)", c->host, port);
+    return fd;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return -1;
+}
+
+static int open_bind_udp_socket(pa_raop_client *c, uint16_t *actual_port) {
+    int fd = -1;
+    uint16_t port;
+    struct sockaddr_in sa4;
+#ifdef HAVE_IPV6
+    struct sockaddr_in6 sa6;
+#endif
+    struct sockaddr *sa;
+    uint16_t *sa_port;
+    socklen_t salen;
+    sa_family_t af;
+    int one = 1;
+
+    pa_assert(actual_port);
+
+    port = *actual_port;
+
+    pa_zero(sa4);
+#ifdef HAVE_IPV6
+    pa_zero(sa6);
+#endif
+    if (inet_pton(AF_INET, pa_rtsp_localip(c->rtsp), &sa4.sin_addr) > 0) {
+        sa4.sin_family = af = AF_INET;
+        sa4.sin_port = htons(port);
+        sa = (struct sockaddr *) &sa4;
+        salen = sizeof(sa4);
+        sa_port = &sa4.sin_port;
+#ifdef HAVE_IPV6
+    } else if (inet_pton(AF_INET6, pa_rtsp_localip(c->rtsp), &sa6.sin6_addr) > 0) {
+        sa6.sin6_family = af = AF_INET6;
+        sa6.sin6_port = htons(port);
+        sa = (struct sockaddr *) &sa6;
+        salen = sizeof(sa6);
+        sa_port = &sa6.sin6_port;
+#endif
+    } else {
+        pa_log("Could not determine which address family to use");
+        goto fail;
+    }
+
+    if ((fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) {
+        pa_log("socket() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+#ifdef SO_TIMESTAMP
+    if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0) {
+        pa_log("setsockopt(SO_TIMESTAMP) failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+#else
+    pa_log("SO_TIMESTAMP unsupported on this platform");
+    goto fail;
+#endif
+
+    one = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
+        pa_log("setsockopt(SO_REUSEADDR) failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    do {
+        *sa_port = htons(port);
+
+        if (bind(fd, sa, salen) < 0 && errno != EADDRINUSE) {
+            pa_log("bind_socket() failed: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+        break;
+    } while (++port > 0);
+
+    pa_log_debug("Socket bound to port %d (SOCK_DGRAM)", port);
+    *actual_port = port;
+
+    return fd;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return -1;
+}
+
+static void tcp_connection_cb(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
+    pa_raop_client *c = userdata;
+
+    pa_assert(sc);
+    pa_assert(c);
+
+    pa_socket_client_unref(sc);
+
+    if (!io) {
+        pa_log("Connection failed: %s", pa_cstrerror(errno));
+        return;
+    }
+
+    c->tcp_sfd = pa_iochannel_get_send_fd(io);
+    pa_iochannel_set_noclose(io, true);
+    pa_make_tcp_socket_low_delay(c->tcp_sfd);
+
+    pa_iochannel_free(io);
+
+    pa_log_debug("Connection established (TCP)");
+
+    if (c->state_callback)
+        c->state_callback(PA_RAOP_CONNECTED, c->state_userdata);
+}
+
+static void rtsp_stream_cb(pa_rtsp_client *rtsp, pa_rtsp_state_t state, pa_rtsp_status_t status, pa_headerlist *headers, void *userdata) {
+    pa_raop_client *c = userdata;
+
+    pa_assert(c);
+    pa_assert(rtsp);
+    pa_assert(rtsp == c->rtsp);
+
+    switch (state) {
+        case STATE_CONNECT: {
+            char *key, *iv, *sdp = NULL;
+            int frames = 0;
+            const char *ip;
+            char *url;
+
+            pa_log_debug("RAOP: CONNECTED");
+
+            ip = pa_rtsp_localip(c->rtsp);
+            url = pa_sprintf_malloc("rtsp://%s/%s", ip, c->sid);
+            pa_rtsp_set_url(c->rtsp, url);
+
+            if (c->protocol == PA_RAOP_PROTOCOL_TCP)
+                frames = FRAMES_PER_TCP_PACKET;
+            else if (c->protocol == PA_RAOP_PROTOCOL_UDP)
+                frames = FRAMES_PER_UDP_PACKET;
+
+            switch(c->encryption) {
+                case PA_RAOP_ENCRYPTION_NONE: {
+                    sdp = pa_sprintf_malloc(
+                        "v=0\r\n"
+                        "o=iTunes %s 0 IN IP4 %s\r\n"
+                        "s=iTunes\r\n"
+                        "c=IN IP4 %s\r\n"
+                        "t=0 0\r\n"
+                        "m=audio 0 RTP/AVP 96\r\n"
+                        "a=rtpmap:96 AppleLossless\r\n"
+                        "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 44100\r\n",
+                        c->sid, ip, c->host, frames);
+
+                    break;
+                }
+
+                case PA_RAOP_ENCRYPTION_RSA:
+                case PA_RAOP_ENCRYPTION_FAIRPLAY:
+                case PA_RAOP_ENCRYPTION_MFISAP:
+                case PA_RAOP_ENCRYPTION_FAIRPLAY_SAP25: {
+                    key = pa_raop_secret_get_key(c->secret);
+                    iv = pa_raop_secret_get_iv(c->secret);
+
+                    sdp = pa_sprintf_malloc(
+                        "v=0\r\n"
+                        "o=iTunes %s 0 IN IP4 %s\r\n"
+                        "s=iTunes\r\n"
+                        "c=IN IP4 %s\r\n"
+                        "t=0 0\r\n"
+                        "m=audio 0 RTP/AVP 96\r\n"
+                        "a=rtpmap:96 AppleLossless\r\n"
+                        "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 44100\r\n"
+                        "a=rsaaeskey:%s\r\n"
+                        "a=aesiv:%s\r\n",
+                        c->sid, ip, c->host, frames, key, iv);
+
+                    pa_xfree(key);
+                    pa_xfree(iv);
+                    break;
+                }
+            }
+
+            pa_rtsp_announce(c->rtsp, sdp);
+
+            pa_xfree(sdp);
+            pa_xfree(url);
+            break;
+        }
+
+        case STATE_OPTIONS: {
+            pa_log_debug("RAOP: OPTIONS (stream cb)");
+
+            break;
+        }
+
+        case STATE_ANNOUNCE: {
+            uint16_t cport = DEFAULT_UDP_CONTROL_PORT;
+            uint16_t tport = DEFAULT_UDP_TIMING_PORT;
+            char *trs = NULL;
+
+            pa_log_debug("RAOP: ANNOUNCE");
+
+            if (c->protocol == PA_RAOP_PROTOCOL_TCP) {
+                trs = pa_sprintf_malloc(
+                    "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
+            } else if (c->protocol == PA_RAOP_PROTOCOL_UDP) {
+                c->udp_cfd = open_bind_udp_socket(c, &cport);
+                c->udp_tfd  = open_bind_udp_socket(c, &tport);
+                if (c->udp_cfd < 0 || c->udp_tfd < 0)
+                    goto annonce_error;
+
+                trs = pa_sprintf_malloc(
+                    "RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;"
+                    "control_port=%d;timing_port=%d",
+                    cport, tport);
+            }
+
+            pa_rtsp_setup(c->rtsp, trs);
+
+            pa_xfree(trs);
+            break;
+
+        annonce_error:
+            if (c->udp_cfd > 0)
+                pa_close(c->udp_cfd);
+            c->udp_cfd = -1;
+            if (c->udp_tfd > 0)
+                pa_close(c->udp_tfd);
+            c->udp_tfd = -1;
+
+            pa_rtsp_client_free(c->rtsp);
+
+            pa_log_error("Aborting RTSP announce, failed creating required sockets");
+
+            c->rtsp = NULL;
+            pa_xfree(trs);
+            break;
+        }
+
+        case STATE_SETUP: {
+            pa_socket_client *sc = NULL;
+            uint32_t sport = DEFAULT_UDP_AUDIO_PORT;
+            uint32_t cport =0, tport = 0;
+            char *ajs, *trs, *token, *pc;
+            const char *token_state = NULL;
+            char delimiters[] = ";";
+
+            pa_log_debug("RAOP: SETUP");
+
+            ajs = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Jack-Status"));
+            trs = pa_xstrdup(pa_headerlist_gets(headers, "Transport"));
+
+            if (ajs) {
+                c->jack_type = JACK_TYPE_ANALOG;
+                c->jack_status = JACK_STATUS_DISCONNECTED;
+
+                while ((token = pa_split(ajs, delimiters, &token_state))) {
+                    if ((pc = strstr(token, "="))) {
+                      *pc = 0;
+                      if (pa_streq(token, "type") && pa_streq(pc + 1, "digital"))
+                          c->jack_type = JACK_TYPE_DIGITAL;
+                    } else {
+                        if (pa_streq(token, "connected"))
+                            c->jack_status = JACK_STATUS_CONNECTED;
+                    }
+                    pa_xfree(token);
+                }
+
+            } else {
+                pa_log_warn("\"Audio-Jack-Status\" missing in RTSP setup response");
+            }
+
+            sport = pa_rtsp_serverport(c->rtsp);
+            if (sport <= 0)
+                goto setup_error;
+
+            token_state = NULL;
+            if (c->protocol == PA_RAOP_PROTOCOL_TCP) {
+                if (!(sc = pa_socket_client_new_string(c->core->mainloop, true, c->host, sport)))
+                    goto setup_error;
+
+                pa_socket_client_ref(sc);
+                pa_socket_client_set_callback(sc, tcp_connection_cb, c);
+
+                pa_socket_client_unref(sc);
+                sc = NULL;
+            } else if (c->protocol == PA_RAOP_PROTOCOL_UDP) {
+                if (trs) {
+                    /* Now parse out the server port component of the response. */
+                    while ((token = pa_split(trs, delimiters, &token_state))) {
+                        if ((pc = strstr(token, "="))) {
+                        *pc = 0;
+                         if (pa_streq(token, "control_port"))
+                                pa_atou(pc + 1, &cport);
+                            if (pa_streq(token, "timing_port"))
+                                pa_atou(pc + 1, &tport);
+                            *pc = '=';
+                        }
+                        pa_xfree(token);
+                    }
+                } else {
+                    pa_log_warn("\"Transport\" missing in RTSP setup response");
+                }
+
+                if (cport <= 0 || tport <= 0)
+                    goto setup_error;
+
+                if ((c->udp_sfd = connect_udp_socket(c, -1, sport)) <= 0)
+                    goto setup_error;
+                if ((c->udp_cfd = connect_udp_socket(c, c->udp_cfd, cport)) <= 0)
+                    goto setup_error;
+                if ((c->udp_tfd = connect_udp_socket(c, c->udp_tfd, tport)) <= 0)
+                    goto setup_error;
+
+                pa_log_debug("Connection established (UDP;control_port=%d;timing_port=%d)", cport, tport);
+
+                if (c->state_callback)
+                    c->state_callback(PA_RAOP_CONNECTED, c->state_userdata);
+            }
+
+            pa_rtsp_record(c->rtsp, &c->seq, &c->rtptime);
+
+            pa_xfree(trs);
+            pa_xfree(ajs);
+            break;
+
+        setup_error:
+            if (c->tcp_sfd > 0)
+                pa_close(c->tcp_sfd);
+            c->tcp_sfd = -1;
+
+            if (c->udp_sfd > 0)
+                pa_close(c->udp_sfd);
+            c->udp_sfd = -1;
+
+            c->udp_cfd = c->udp_tfd = -1;
+
+            pa_rtsp_client_free(c->rtsp);
+
+            pa_log_error("aborting RTSP setup, failed creating required sockets");
+
+            if (c->state_callback)
+                c->state_callback(PA_RAOP_DISCONNECTED, c->state_userdata);
+
+            c->rtsp = NULL;
+            break;
+        }
+
+        case STATE_RECORD: {
+            int32_t latency = 0;
+            uint32_t ssrc;
+            char *alt;
+
+            pa_log_debug("RAOP: RECORD");
+
+            alt = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Latency"));
+            if (alt)
+                pa_atoi(alt, &latency);
+
+            pa_random(&ssrc, sizeof(ssrc));
+            c->is_first_packet = true;
+            c->is_recording = true;
+            c->sync_count = 0;
+            c->ssrc = ssrc;
+
+            if (c->state_callback)
+                c->state_callback((int) PA_RAOP_RECORDING, c->state_userdata);
+
+            pa_xfree(alt);
+            break;
+        }
+
+        case STATE_SET_PARAMETER: {
+            pa_log_debug("RAOP: SET_PARAMETER");
+
+            break;
+        }
+
+        case STATE_FLUSH: {
+            pa_log_debug("RAOP: FLUSHED");
+
+            c->is_recording = false;
+
+            break;
+        }
+
+        case STATE_TEARDOWN: {
+            pa_log_debug("RAOP: TEARDOWN");
+
+            c->is_recording = false;
+
+            if (c->pbuf)
+                pa_raop_pb_clear(c->pbuf);
+
+            if (c->tcp_sfd > 0)
+                pa_close(c->tcp_sfd);
+            c->tcp_sfd = -1;
+
+            if (c->udp_sfd > 0)
+                pa_close(c->udp_sfd);
+            c->udp_sfd = -1;
+
+            /* Polling sockets will be closed by sink */
+            c->udp_cfd = c->udp_tfd = -1;
+            c->tcp_sfd = -1;
+
+            pa_rtsp_client_free(c->rtsp);
+            pa_xfree(c->sid);
+            c->rtsp = NULL;
+            c->sid = NULL;
+
+            if (c->state_callback)
+                c->state_callback(PA_RAOP_DISCONNECTED, c->state_userdata);
+
+            break;
+        }
+
+        case STATE_DISCONNECTED: {
+            pa_log_debug("RAOP: DISCONNECTED");
+
+            c->is_recording = false;
+
+            if (c->pbuf)
+                pa_raop_pb_clear(c->pbuf);
+
+            if (c->tcp_sfd > 0)
+                pa_close(c->tcp_sfd);
+            c->tcp_sfd = -1;
+
+            if (c->udp_sfd > 0)
+                pa_close(c->udp_sfd);
+            c->udp_sfd = -1;
+
+            /* Polling sockets will be closed by sink */
+            c->udp_cfd = c->udp_tfd = -1;
+            c->tcp_sfd = -1;
+
+            pa_log_error("RTSP control channel closed (disconnected)");
+
+            pa_rtsp_client_free(c->rtsp);
+            pa_xfree(c->sid);
+            c->rtsp = NULL;
+            c->sid = NULL;
+
+            if (c->state_callback)
+                c->state_callback((int) PA_RAOP_DISCONNECTED, c->state_userdata);
+
+            break;
+        }
+    }
+}
+
+static void rtsp_auth_cb(pa_rtsp_client *rtsp, pa_rtsp_state_t state, pa_rtsp_status_t status, pa_headerlist *headers, void *userdata) {
+    pa_raop_client *c = userdata;
+
+    pa_assert(c);
+    pa_assert(rtsp);
+    pa_assert(rtsp == c->rtsp);
+
+    switch (state) {
+        case STATE_CONNECT: {
+            char *sci = NULL, *sac = NULL;
+            uint16_t rac;
+            struct {
+                uint32_t ci1;
+                uint32_t ci2;
+            } rci;
+
+            pa_random(&rci, sizeof(rci));
+            /* Generate a random Client-Instance number */
+            sci = pa_sprintf_malloc("%08x%08x",rci.ci1, rci.ci2);
+            pa_rtsp_add_header(c->rtsp, "Client-Instance", sci);
+
+            pa_random(&rac, sizeof(rac));
+            /* Generate a random Apple-Challenge key */
+            pa_raop_base64_encode(&rac, 8 * sizeof(rac), &sac);
+            rtrim_char(sac, '=');
+            pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
+
+            pa_rtsp_options(c->rtsp);
+
+            pa_xfree(sac);
+            pa_xfree(sci);
+            break;
+        }
+
+        case STATE_OPTIONS: {
+            static bool waiting = false;
+            const char *current = NULL;
+            char space[] = " ";
+            char *token,*ath = NULL;
+            char *publ, *wath, *mth, *val;
+            char *realm = NULL, *nonce = NULL, *response = NULL;
+            char comma[] = ",";
+
+            pa_log_debug("RAOP: OPTIONS (auth cb)");
+            /* We do not consider the Apple-Response */
+            pa_rtsp_remove_header(c->rtsp, "Apple-Challenge");
+
+            if (STATUS_UNAUTHORIZED == status) {
+                wath = pa_xstrdup(pa_headerlist_gets(headers, "WWW-Authenticate"));
+                if (true == waiting) {
+                    pa_xfree(wath);
+                    goto fail;
+                }
+
+                if (wath)
+                    mth = pa_split(wath, space, &current);
+                while ((token = pa_split(wath, comma, &current))) {
+                    val = NULL;
+                    if ((val = strstr(token, "="))) {
+                        if (NULL == realm && val > strstr(token, "realm"))
+                            realm = pa_xstrdup(val + 2);
+                        else if (NULL == nonce && val > strstr(token, "nonce"))
+                            nonce = pa_xstrdup(val + 2);
+                        val = NULL;
+                    }
+
+                    pa_xfree(token);
+                }
+
+                if (pa_safe_streq(mth, "Basic")) {
+                    rtrim_char(realm, '\"');
+
+                    pa_raop_basic_response(DEFAULT_USER_NAME, c->password, &response);
+                    ath = pa_sprintf_malloc("Basic %s",
+                        response);
+
+                    pa_xfree(response);
+                    pa_xfree(realm);
+                } else if (pa_safe_streq(mth, "Digest")) {
+                    rtrim_char(realm, '\"');
+                    rtrim_char(nonce, '\"');
+
+                    pa_raop_digest_response(DEFAULT_USER_NAME, realm, c->password, nonce, "*", &response);
+                    ath = pa_sprintf_malloc("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"*\", response=\"%s\"",
+                        DEFAULT_USER_NAME, realm, nonce,
+                        response);
+
+                    pa_xfree(response);
+                    pa_xfree(realm);
+                    pa_xfree(nonce);
+                } else {
+                    pa_log_error("unsupported authentication method: %s", mth);
+                    pa_xfree(wath);
+                    pa_xfree(mth);
+                    goto error;
+                }
+
+                pa_xfree(wath);
+                pa_xfree(mth);
+
+                pa_rtsp_add_header(c->rtsp, "Authorization", ath);
+                pa_xfree(ath);
+
+                waiting = true;
+                pa_rtsp_options(c->rtsp);
+                break;
+            }
+
+            if (STATUS_OK == status) {
+                publ = pa_xstrdup(pa_headerlist_gets(headers, "Public"));
+                c->sci = pa_xstrdup(pa_rtsp_get_header(c->rtsp, "Client-Instance"));
+
+                if (c->password)
+                    pa_xfree(c->password);
+                pa_xfree(publ);
+                c->password = NULL;
+            }
+
+            if (c->state_callback)
+                c->state_callback((int) PA_RAOP_AUTHENTICATED, c->state_userdata);
+            pa_rtsp_client_free(c->rtsp);
+            c->rtsp = NULL;
+
+            waiting = false;
+            break;
+
+        fail:
+            if (c->state_callback)
+                c->state_callback((int) PA_RAOP_DISCONNECTED, c->state_userdata);
+            pa_rtsp_client_free(c->rtsp);
+            c->rtsp = NULL;
+
+            pa_log_error("aborting authentication, wrong password");
+
+            waiting = false;
+            break;
+
+        error:
+            if (c->state_callback)
+                c->state_callback((int) PA_RAOP_DISCONNECTED, c->state_userdata);
+            pa_rtsp_client_free(c->rtsp);
+            c->rtsp = NULL;
+
+            pa_log_error("aborting authentication, unexpected failure");
+
+            waiting = false;
+            break;
+        }
+
+        case STATE_ANNOUNCE:
+        case STATE_SETUP:
+        case STATE_RECORD:
+        case STATE_SET_PARAMETER:
+        case STATE_FLUSH:
+        case STATE_TEARDOWN:
+        case STATE_DISCONNECTED:
+        default: {
+            if (c->state_callback)
+                c->state_callback((int) PA_RAOP_DISCONNECTED, c->state_userdata);
+            pa_rtsp_client_free(c->rtsp);
+            c->rtsp = NULL;
+
+            if (c->sci)
+                pa_xfree(c->sci);
+            c->sci = NULL;
+
+            break;
+        }
+    }
+}
+
+pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_protocol_t protocol,
+                                   pa_raop_encryption_t encryption, pa_raop_codec_t codec) {
+    pa_raop_client *c;
+
+    pa_parsed_address a;
+    pa_sample_spec ss;
+
+    pa_assert(core);
+    pa_assert(host);
+
+    if (pa_parse_address(host, &a) < 0)
+        return NULL;
+
+    if (a.type == PA_PARSED_ADDRESS_UNIX) {
+        pa_xfree(a.path_or_host);
+        return NULL;
+    }
+
+    c = pa_xnew0(pa_raop_client, 1);
+    c->core = core;
+    c->host = pa_xstrdup(a.path_or_host);
+    if (a.port > 0)
+        c->port = a.port;
+    else
+        c->port = DEFAULT_RAOP_PORT;
+    c->rtsp = NULL;
+    c->sci = c->sid = NULL;
+    c->password = NULL;
+
+    c->protocol = protocol;
+    c->encryption = encryption;
+    c->codec = codec;
+
+    c->tcp_sfd = -1;
+
+    c->udp_sfd = -1;
+    c->udp_cfd = -1;
+    c->udp_tfd = -1;
+
+    c->secret = NULL;
+    if (c->encryption != PA_RAOP_ENCRYPTION_NONE)
+        c->secret = pa_raop_secret_new();
+
+    ss = core->default_sample_spec;
+
+    c->is_recording = false;
+    c->is_first_packet = true;
+    /* Packet sync interval should be around 1s (UDP only) */
+    c->sync_interval = ss.rate / FRAMES_PER_UDP_PACKET;
+    c->sync_count = 0;
+
+    c->pbuf = pa_raop_pb_new(UDP_DEFAULT_PKT_BUF_SIZE);
+
+    return c;
+}
+
+void pa_raop_client_free(pa_raop_client *c) {
+    pa_assert(c);
+
+    pa_raop_pb_delete(c->pbuf);
+
+    pa_xfree(c->sid);
+    pa_xfree(c->sci);
+    if (c->secret)
+        pa_raop_secret_free(c->secret);
+    pa_xfree(c->password);
+    c->sci = c->sid = NULL;
+    c->password = NULL;
+    c->secret = NULL;
+
+    if (c->rtsp)
+        pa_rtsp_client_free(c->rtsp);
+    c->rtsp = NULL;
+
+    pa_xfree(c->host);
+    pa_xfree(c);
+}
+
+int pa_raop_client_authenticate (pa_raop_client *c, const char *password) {
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (c->rtsp || c->password) {
+        pa_log_debug("Authentication/Connection already in progress...");
+        return 0;
+    }
+
+    c->password = NULL;
+    if (password)
+        c->password = pa_xstrdup(password);
+    c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, DEFAULT_USER_AGENT);
+
+    pa_assert(c->rtsp);
+
+    pa_rtsp_set_callback(c->rtsp, rtsp_auth_cb, c);
+    rv = pa_rtsp_connect(c->rtsp);
+    return rv;
+}
+
+bool pa_raop_client_is_authenticated(pa_raop_client *c) {
+    pa_assert(c);
+
+    return (c->sci != NULL);
+}
+
+int pa_raop_client_announce(pa_raop_client *c) {
+    uint32_t sid;
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (c->rtsp) {
+        pa_log_debug("Connection already in progress...");
+        return 0;
+    } else if (!c->sci) {
+        pa_log_debug("ANNOUNCE requires a preliminary authentication");
+        return 1;
+    }
+
+    c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, DEFAULT_USER_AGENT);
+
+    pa_assert(c->rtsp);
+
+    c->sync_count = 0;
+    c->is_recording = false;
+    c->is_first_packet = true;
+    pa_random(&sid, sizeof(sid));
+    c->sid = pa_sprintf_malloc("%u", sid);
+    pa_rtsp_set_callback(c->rtsp, rtsp_stream_cb, c);
+
+    rv = pa_rtsp_connect(c->rtsp);
+    return rv;
+}
+
+bool pa_raop_client_is_alive(pa_raop_client *c) {
+    pa_assert(c);
+
+    if (!c->rtsp || !c->sci) {
+        pa_log_debug("Not alive, connection not established yet...");
+        return false;
+    }
+
+    switch (c->protocol) {
+        case PA_RAOP_PROTOCOL_TCP:
+            if (c->tcp_sfd > 0)
+                return true;
+            break;
+        case PA_RAOP_PROTOCOL_UDP:
+            if (c->udp_sfd > 0)
+                return true;
+            break;
+        default:
+            break;
+    }
+
+    return false;
+}
+
+bool pa_raop_client_can_stream(pa_raop_client *c) {
+    pa_assert(c);
+
+    if (!c->rtsp || !c->sci) {
+        pa_log_debug("Can't stream, connection not established yet...");
+        return false;
+    }
+
+    switch (c->protocol) {
+        case PA_RAOP_PROTOCOL_TCP:
+            if (c->tcp_sfd > 0 && c->is_recording)
+                return true;
+            break;
+        case PA_RAOP_PROTOCOL_UDP:
+            if (c->udp_sfd > 0 && c->is_recording)
+                return true;
+            break;
+        default:
+            break;
+    }
+
+    return false;
+}
+
+int pa_raop_client_stream(pa_raop_client *c) {
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (!c->rtsp || !c->sci) {
+        pa_log_debug("Streaming's impossible, connection not established yet...");
+        return 0;
+    }
+
+    switch (c->protocol) {
+        case PA_RAOP_PROTOCOL_TCP:
+            if (c->tcp_sfd > 0 && !c->is_recording) {
+                c->is_recording = true;
+                c->is_first_packet = true;
+                c->sync_count = 0;
+            }
+            break;
+        case PA_RAOP_PROTOCOL_UDP:
+            if (c->udp_sfd > 0 && !c->is_recording) {
+                c->is_recording = true;
+                c->is_first_packet = true;
+                c->sync_count = 0;
+            }
+            break;
+        default:
+            rv = 1;
+            break;
+    }
+
+    return rv;
+}
+
+int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume) {
+    char *param;
+    int rv = 0;
+    double db;
+
+    pa_assert(c);
+
+    if (!c->rtsp) {
+        pa_log_debug("Cannot SET_PARAMETER, connection not established yet...");
+        return 0;
+    } else if (!c->sci) {
+        pa_log_debug("SET_PARAMETER requires a preliminary authentication");
+        return 1;
+    }
+
+    db = pa_sw_volume_to_dB(volume);
+    if (db < VOLUME_MIN)
+        db = VOLUME_MIN;
+    else if (db > VOLUME_MAX)
+        db = VOLUME_MAX;
+
+    pa_log_debug("volume=%u db=%.6f", volume, db);
+
+    param = pa_sprintf_malloc("volume: %0.6f\r\n", db);
+    /* We just hit and hope, cannot wait for the callback. */
+    if (c->rtsp != NULL && pa_rtsp_exec_ready(c->rtsp))
+        rv = pa_rtsp_setparameter(c->rtsp, param);
+
+    pa_xfree(param);
+    return rv;
+}
+
+int pa_raop_client_flush(pa_raop_client *c) {
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (!c->rtsp) {
+        pa_log_debug("Cannot FLUSH, connection not established yet...)");
+        return 0;
+    } else if (!c->sci) {
+        pa_log_debug("FLUSH requires a preliminary authentication");
+        return 1;
+    }
+
+    rv = pa_rtsp_flush(c->rtsp, c->seq, c->rtptime);
+    return rv;
+}
+
+int pa_raop_client_teardown(pa_raop_client *c) {
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (!c->rtsp) {
+        pa_log_debug("Cannot TEARDOWN, connection not established yet...");
+        return 0;
+    } else if (!c->sci) {
+        pa_log_debug("TEARDOWN requires a preliminary authentication");
+        return 1;
+    }
+
+    rv = pa_rtsp_teardown(c->rtsp);
+    return rv;
+}
+
+void pa_raop_client_get_frames_per_block(pa_raop_client *c, size_t *frames) {
+    pa_assert(c);
+    pa_assert(frames);
+
+    switch (c->protocol) {
+        case PA_RAOP_PROTOCOL_TCP:
+            *frames = FRAMES_PER_TCP_PACKET;
+            break;
+        case PA_RAOP_PROTOCOL_UDP:
+            *frames = FRAMES_PER_UDP_PACKET;
+            break;
+        default:
+            *frames = 0;
+            break;
+    }
+}
+
+bool pa_raop_client_register_pollfd(pa_raop_client *c, pa_rtpoll *poll, pa_rtpoll_item **poll_item) {
+    struct pollfd *pollfd = NULL;
+    pa_rtpoll_item *item = NULL;
+    bool oob = true;
+
+    pa_assert(c);
+    pa_assert(poll);
+    pa_assert(poll_item);
+
+    switch (c->protocol) {
+        case PA_RAOP_PROTOCOL_TCP:
+            item = pa_rtpoll_item_new(poll, PA_RTPOLL_NEVER, 1);
+            pollfd = pa_rtpoll_item_get_pollfd(item, NULL);
+            pollfd->fd = c->tcp_sfd;
+            pollfd->events = POLLOUT;
+            pollfd->revents = 0;
+            *poll_item = item;
+            oob = false;
+            break;
+        case PA_RAOP_PROTOCOL_UDP:
+            item = pa_rtpoll_item_new(poll, PA_RTPOLL_NEVER, 2);
+            pollfd = pa_rtpoll_item_get_pollfd(item, NULL);
+            pollfd->fd = c->udp_cfd;
+            pollfd->events = POLLIN | POLLPRI;
+            pollfd->revents = 0;
+            pollfd++;
+            pollfd->fd = c->udp_tfd;
+            pollfd->events = POLLIN | POLLPRI;
+            pollfd->revents = 0;
+            *poll_item = item;
+            oob = true;
+            break;
+        default:
+            *poll_item = NULL;
+            break;
+    }
+
+    return oob;
+}
+
+pa_volume_t pa_raop_client_adjust_volume(pa_raop_client *c, pa_volume_t volume) {
+    double minv, maxv;
+
+    pa_assert(c);
+
+    if (c->protocol != PA_RAOP_PROTOCOL_UDP)
+        return volume;
+
+    maxv = pa_sw_volume_from_dB(0.0);
+    minv = maxv * pow(10.0, VOLUME_DEF / 60.0);
+
+    /* Adjust volume so that it fits into VOLUME_DEF <= v <= 0 dB */
+    return volume - volume * (minv / maxv) + minv;
+}
+
+void pa_raop_client_handle_oob_packet(pa_raop_client *c, const int fd, const uint8_t packet[], ssize_t size) {
+    pa_assert(c);
+    pa_assert(fd > 0);
+    pa_assert(packet);
+
+    if (c->protocol == PA_RAOP_PROTOCOL_UDP) {
+        if (fd == c->udp_cfd) {
+            pa_log_debug("Received UDP control packet");
+            handle_udp_control_packet(c, packet, size);
+        } else if (fd == c->udp_tfd) {
+            pa_log_debug("Received UDP timing packet");
+            handle_udp_timing_packet(c, packet, size);
+        }
+    }
+}
+
+ssize_t pa_raop_client_send_audio_packet(pa_raop_client *c, pa_memchunk *block, size_t offset) {
+    ssize_t written = 0;
+
+    pa_assert(c);
+    pa_assert(block);
+
+    /* Sync RTP & NTP timestamp if required (UDP). */
+    if (c->protocol == PA_RAOP_PROTOCOL_UDP) {
+        c->sync_count++;
+        if (c->is_first_packet || c->sync_count >= c->sync_interval) {
+            send_udp_sync_packet(c, c->rtptime);
+            c->sync_count = 0;
+        }
+    }
+
+    switch (c->protocol) {
+        case PA_RAOP_PROTOCOL_TCP:
+            written = send_tcp_audio_packet(c, block, offset);
+            break;
+        case PA_RAOP_PROTOCOL_UDP:
+            written = send_udp_audio_packet(c, block, offset);
+            break;
+        default:
+            written = -1;
+            break;
+    }
+
+    c->is_first_packet = false;
+    return written;
+}
+
+void pa_raop_client_set_state_callback(pa_raop_client *c, pa_raop_client_state_cb_t callback, void *userdata) {
+    pa_assert(c);
+
+    c->state_callback = callback;
+    c->state_userdata = userdata;
+}
diff --git a/src/modules/raop/raop-client.h b/src/modules/raop/raop-client.h
new file mode 100644
index 0000000..72e6018
--- /dev/null
+++ b/src/modules/raop/raop-client.h
@@ -0,0 +1,83 @@
+#ifndef fooraopclientfoo
+#define fooraopclientfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio 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 Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/volume.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/rtpoll.h>
+
+typedef enum pa_raop_protocol {
+    PA_RAOP_PROTOCOL_TCP,
+    PA_RAOP_PROTOCOL_UDP
+} pa_raop_protocol_t;
+
+typedef enum pa_raop_encryption {
+    PA_RAOP_ENCRYPTION_NONE,
+    PA_RAOP_ENCRYPTION_RSA,
+    PA_RAOP_ENCRYPTION_FAIRPLAY,
+    PA_RAOP_ENCRYPTION_MFISAP,
+    PA_RAOP_ENCRYPTION_FAIRPLAY_SAP25
+} pa_raop_encryption_t;
+
+typedef enum pa_raop_codec {
+    PA_RAOP_CODEC_PCM,
+    PA_RAOP_CODEC_ALAC,
+    PA_RAOP_CODEC_AAC,
+    PA_RAOP_CODEC_AAC_ELD
+} pa_raop_codec_t;
+
+typedef struct pa_raop_client pa_raop_client;
+
+typedef enum pa_raop_state {
+    PA_RAOP_INVALID_STATE,
+    PA_RAOP_AUTHENTICATED,
+    PA_RAOP_CONNECTED,
+    PA_RAOP_RECORDING,
+    PA_RAOP_DISCONNECTED
+} pa_raop_state_t;
+
+pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_protocol_t protocol,
+                                   pa_raop_encryption_t encryption, pa_raop_codec_t codec);
+void pa_raop_client_free(pa_raop_client *c);
+
+int pa_raop_client_authenticate(pa_raop_client *c, const char *password);
+bool pa_raop_client_is_authenticated(pa_raop_client *c);
+
+int pa_raop_client_announce(pa_raop_client *c);
+bool pa_raop_client_is_alive(pa_raop_client *c);
+bool pa_raop_client_can_stream(pa_raop_client *c);
+int pa_raop_client_stream(pa_raop_client *c);
+int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume);
+int pa_raop_client_flush(pa_raop_client *c);
+int pa_raop_client_teardown(pa_raop_client *c);
+
+void pa_raop_client_get_frames_per_block(pa_raop_client *c, size_t *size);
+bool pa_raop_client_register_pollfd(pa_raop_client *c, pa_rtpoll *poll, pa_rtpoll_item **poll_item);
+pa_volume_t pa_raop_client_adjust_volume(pa_raop_client *c, pa_volume_t volume);
+void pa_raop_client_handle_oob_packet(pa_raop_client *c, const int fd, const uint8_t packet[], ssize_t size);
+ssize_t pa_raop_client_send_audio_packet(pa_raop_client *c, pa_memchunk *block, size_t offset);
+
+typedef void (*pa_raop_client_state_cb_t)(pa_raop_state_t state, void *userdata);
+void pa_raop_client_set_state_callback(pa_raop_client *c, pa_raop_client_state_cb_t callback, void *userdata);
+
+#endif
diff --git a/src/modules/raop/raop-crypto.c b/src/modules/raop/raop-crypto.c
new file mode 100644
index 0000000..0dd6463
--- /dev/null
+++ b/src/modules/raop/raop-crypto.c
@@ -0,0 +1,164 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio 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 Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <openssl/err.h>
+#include <openssl/aes.h>
+#include <openssl/rsa.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/random.h>
+
+#include "raop-crypto.h"
+#include "raop-util.h"
+
+#define AES_CHUNK_SIZE 16
+
+/* Openssl 1.1.0 broke compatibility. Before 1.1.0 we had to set RSA->n and
+ * RSA->e manually, but after 1.1.0 the RSA struct is opaque and we have to use
+ * RSA_set0_key(). RSA_set0_key() is a new function added in 1.1.0. We could
+ * depend on openssl 1.1.0, but it may take some time before distributions will
+ * be able to upgrade to the new openssl version. To insulate ourselves from
+ * such transition problems, let's implement RSA_set0_key() ourselves if it's
+ * not available. */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) {
+    r->n = n;
+    r->e = e;
+    return 1;
+}
+#endif
+
+struct pa_raop_secret {
+    uint8_t key[AES_CHUNK_SIZE]; /* Key for aes-cbc */
+    uint8_t iv[AES_CHUNK_SIZE];  /* Initialization vector for cbc */
+    AES_KEY aes;                 /* AES encryption */
+};
+
+static const char rsa_modulus[] =
+    "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC"
+    "5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDR"
+    "KSKv6kDqnw4UwPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuB"
+    "OitnZ/bDzPHrTOZz0Dew0uowxf/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJ"
+    "Q+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/UAaHqn9JdsBWLUEpVviYnh"
+    "imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew==";
+
+static const char rsa_exponent[] =
+    "AQAB";
+
+static int rsa_encrypt(uint8_t *data, int len, uint8_t *str) {
+    uint8_t modules[256];
+    uint8_t exponent[8];
+    int size;
+    RSA *rsa;
+    BIGNUM *n_bn;
+    BIGNUM *e_bn;
+
+    pa_assert(data);
+    pa_assert(str);
+
+    rsa = RSA_new();
+    size = pa_raop_base64_decode(rsa_modulus, modules);
+    n_bn = BN_bin2bn(modules, size, NULL);
+    size = pa_raop_base64_decode(rsa_exponent, exponent);
+    e_bn = BN_bin2bn(exponent, size, NULL);
+    RSA_set0_key(rsa, n_bn, e_bn, NULL);
+
+    size = RSA_public_encrypt(len, data, str, rsa, RSA_PKCS1_OAEP_PADDING);
+
+    RSA_free(rsa);
+    return size;
+}
+
+pa_raop_secret* pa_raop_secret_new(void) {
+    pa_raop_secret *s = pa_xnew0(pa_raop_secret, 1);
+
+    pa_assert(s);
+
+    pa_random(s->key, sizeof(s->key));
+    AES_set_encrypt_key(s->key, 128, &s->aes);
+    pa_random(s->iv, sizeof(s->iv));
+
+    return s;
+}
+
+void pa_raop_secret_free(pa_raop_secret *s) {
+    pa_assert(s);
+
+    pa_xfree(s);
+}
+
+char* pa_raop_secret_get_iv(pa_raop_secret *s) {
+    char *base64_iv = NULL;
+
+    pa_assert(s);
+
+    pa_raop_base64_encode(s->iv, AES_CHUNK_SIZE, &base64_iv);
+
+    return base64_iv;
+}
+
+char* pa_raop_secret_get_key(pa_raop_secret *s) {
+    char *base64_key = NULL;
+    uint8_t rsa_key[512];
+    int size = 0;
+
+    pa_assert(s);
+
+    /* Encrypt our AES public key to send to the device */
+    size = rsa_encrypt(s->key, AES_CHUNK_SIZE, rsa_key);
+    pa_raop_base64_encode(rsa_key, size, &base64_key);
+
+    return base64_key;
+}
+
+int pa_raop_aes_encrypt(pa_raop_secret *s, uint8_t *data, int len) {
+    static uint8_t nv[AES_CHUNK_SIZE];
+    uint8_t *buffer;
+    int i = 0, j;
+
+    pa_assert(s);
+    pa_assert(data);
+
+    memcpy(nv, s->iv, AES_CHUNK_SIZE);
+
+    while (i + AES_CHUNK_SIZE <= len) {
+        buffer = data + i;
+        for (j = 0; j < AES_CHUNK_SIZE; ++j)
+            buffer[j] ^= nv[j];
+
+        AES_encrypt(buffer, buffer, &s->aes);
+
+        memcpy(nv, buffer, AES_CHUNK_SIZE);
+        i += AES_CHUNK_SIZE;
+    }
+
+    return i;
+}
diff --git a/src/modules/raop/raop-crypto.h b/src/modules/raop/raop-crypto.h
new file mode 100644
index 0000000..65f7577
--- /dev/null
+++ b/src/modules/raop/raop-crypto.h
@@ -0,0 +1,35 @@
+#ifndef fooraopcryptofoo
+#define fooraopcryptofoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio 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 Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+typedef struct pa_raop_secret pa_raop_secret;
+
+pa_raop_secret* pa_raop_secret_new(void);
+void pa_raop_secret_free(pa_raop_secret *s);
+
+char* pa_raop_secret_get_iv(pa_raop_secret *s);
+char* pa_raop_secret_get_key(pa_raop_secret *s);
+
+int pa_raop_aes_encrypt(pa_raop_secret *s, uint8_t *data, int len);
+
+#endif
diff --git a/src/modules/raop/raop-packet-buffer.c b/src/modules/raop/raop-packet-buffer.c
new file mode 100644
index 0000000..ef8ea15
--- /dev/null
+++ b/src/modules/raop/raop-packet-buffer.c
@@ -0,0 +1,172 @@
+/***
+  Circular buffer for RTP audio packets with random access support
+  by RTP sequence number.
+
+  Copyright 2013 Matthias Wabersich, Hajime Fujita
+
+  This is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  This 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 Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+
+***/
+
+#include <stdlib.h>
+#include <limits.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-error.h>
+#include "raop-client.h"
+
+#include "raop-packet-buffer.h"
+
+/* FRAMES_PER_PACKET*2*2 + sizeof(udp_audio_header) + sizeof(ALAC header), unencoded */
+#define PACKET_SIZE_MAX (352*2*2 + 12 + 7) /* FIXME; hardcoded constant ! */
+/* Header room for packet retransmission header */
+#define RETRANS_HEADER_ROOM 4
+
+/* Packet element */
+struct pa_raop_packet_element {
+    uint16_t  seq_num; /* RTP sequence number (in host byte order) */
+    ssize_t   length;  /* Actual packet length */
+    /* Packet data including RTP header */
+    uint8_t   data[PACKET_SIZE_MAX + RETRANS_HEADER_ROOM];
+};
+
+/* Buffer struct */
+struct pa_raop_packet_buffer {
+    size_t   size;          /* max number of packets in buffer */
+    size_t   start;         /* index of oldest packet */
+    size_t   count;         /* number of packets in buffer */
+    uint16_t first_seq_num; /* Sequence number of first packet in buffer */
+    uint16_t latest_seq_num; /* Debug purpose */
+    pa_raop_packet_element *packets; /* Packet element pointer */
+};
+
+pa_raop_packet_buffer *pa_raop_pb_new(size_t size) {
+    pa_raop_packet_buffer *pb = pa_xmalloc0(sizeof(*pb));
+
+    pb->size = size;
+    pb->packets = (pa_raop_packet_element *)
+        pa_xmalloc(size * sizeof(pa_raop_packet_element));
+
+    pa_raop_pb_clear(pb);
+
+    return pb;
+}
+
+void pa_raop_pb_clear(pa_raop_packet_buffer *pb) {
+    pb->start = 0;
+    pb->count = 0;
+    pb->first_seq_num = 0;
+    pb->latest_seq_num = 0;
+    memset(pb->packets, 0, pb->size * sizeof(pa_raop_packet_element));
+}
+
+void pa_raop_pb_delete(pa_raop_packet_buffer *pb) {
+    pa_xfree(pb->packets);
+    pa_xfree(pb);
+}
+
+static int pb_is_full(pa_raop_packet_buffer *pb) {
+    return pb->count == pb->size;
+}
+
+static int pb_is_empty(pa_raop_packet_buffer *pb) {
+    return pb->count == 0;
+}
+
+static pa_raop_packet_element *pb_prepare_write(pa_raop_packet_buffer *pb, uint16_t seq) {
+    size_t end = (pb->start + pb->count) % pb->size;
+    pa_raop_packet_element *packet;
+
+    /* Set first packet sequence number in buffer if buffer is empty */
+    if (pb_is_empty(pb))
+        pb->first_seq_num = seq;
+    else
+        pa_assert((uint16_t) (pb->latest_seq_num + 1) == seq);
+
+    packet = &pb->packets[end];
+
+    if (pb_is_full(pb)) {
+        pb->start = (pb->start + 1) % pb->size; /* full, overwrite */
+
+        /* Set first packet sequence number in buffer
+           to new start packet sequence number */
+        pb->first_seq_num = pb->packets[pb->start].seq_num;
+    } else
+        ++ pb->count;
+
+    pb->latest_seq_num = seq;
+
+    return packet;
+}
+
+/* Write packet data to packet buffer */
+void pa_raop_pb_write_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, const uint8_t *packet_data, ssize_t packet_length) {
+    pa_raop_packet_element *packet;
+
+    pa_assert(pb);
+    pa_assert(packet_data);
+    pa_assert(packet_length <= PACKET_SIZE_MAX);
+
+    packet = pb_prepare_write(pb, seq_num);
+    packet->seq_num = seq_num;
+    packet->length = packet_length + RETRANS_HEADER_ROOM;
+
+    /* Insert RETRANS_HEADER_ROOM bytes in front of packet data,
+       for retransmission header */
+    memset(packet->data, 0, RETRANS_HEADER_ROOM);
+    memcpy(packet->data + RETRANS_HEADER_ROOM, packet_data, packet_length);
+}
+
+/* l < r?, considers wrapping */
+static bool seq_lt(uint16_t l, uint16_t r) {
+    return l - r > USHRT_MAX/2;
+}
+
+/* Random access to packet from buffer by sequence number for (re-)sending. */
+ssize_t pa_raop_pb_read_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, uint8_t **packet_data) {
+    uint16_t index = 0; /* Index of requested packet */
+    pa_raop_packet_element *packet;
+
+    /* If the buffer is empty, there is no use in calculating indices */
+    if (pb_is_empty(pb))
+        return -1;
+
+    /* If the requested packet is too old (seq_num below first seq number
+       in buffer) or too young (seq_num greater than current seq number),
+       do nothing and return */
+    if (seq_lt(seq_num, pb->first_seq_num))
+        return -1;
+
+    index = (uint16_t) (seq_num - pb->first_seq_num);
+    if (index >= pb->count)
+        return -1;
+
+    /*  Index of the requested packet in the buffer is calculated
+        using the first sequence number stored in the buffer.
+        The offset (seq_num - first_seq_num) is used to access the array. */
+    packet = &pb->packets[(pb->start + index) % pb->size];
+
+    pa_assert(packet->data[RETRANS_HEADER_ROOM + 2] == (seq_num >> 8));
+    pa_assert(packet->data[RETRANS_HEADER_ROOM + 3] == (seq_num & 0xff));
+    pa_assert(packet_data);
+
+    *packet_data = packet->data;
+
+    return packet->length;
+}
diff --git a/src/modules/raop/raop-packet-buffer.h b/src/modules/raop/raop-packet-buffer.h
new file mode 100644
index 0000000..d8a08a0
--- /dev/null
+++ b/src/modules/raop/raop-packet-buffer.h
@@ -0,0 +1,42 @@
+#ifndef RAOP_PACKET_BUFFER_H_INCLUDED
+#define RAOP_PACKET_BUFFER_H_INCLUDED
+
+/***
+  Circular buffer for RTP audio packets with random access support
+  by RTP sequence number.
+
+  Copyright 2013 Matthias Wabersich, Hajime Fujita
+
+  This is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  This 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 Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+
+***/
+
+struct pa_raop_packet_element;
+typedef struct pa_raop_packet_element pa_raop_packet_element;
+
+struct pa_raop_packet_buffer;
+typedef struct pa_raop_packet_buffer pa_raop_packet_buffer;
+
+/* Allocates a new circular packet buffer
+   size: Maximum number of packets to store */
+pa_raop_packet_buffer *pa_raop_pb_new(size_t size);
+void pa_raop_pb_clear(pa_raop_packet_buffer *pb);
+void pa_raop_pb_delete(pa_raop_packet_buffer *pb);
+
+void pa_raop_pb_write_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, const uint8_t *packet_data, ssize_t packet_length);
+ssize_t pa_raop_pb_read_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, uint8_t **packet_data);
+
+#endif /* RAOP_PACKET_BUFFER_H_INCLUDED */
diff --git a/src/modules/raop/raop-sink.c b/src/modules/raop/raop-sink.c
new file mode 100644
index 0000000..fdec0f2
--- /dev/null
+++ b/src/modules/raop/raop-sink.c
@@ -0,0 +1,670 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2008 Colin Guthrie
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio 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 Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_LINUX_SOCKIOS_H
+#include <linux/sockios.h>
+#endif
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/volume.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/module.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/poll.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/time-smoother.h>
+
+#include "raop-sink.h"
+#include "raop-client.h"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+    pa_rtpoll_item *rtpoll_item;
+    bool oob;
+
+    pa_raop_client *raop;
+    pa_raop_protocol_t protocol;
+    pa_raop_encryption_t encryption;
+    pa_raop_codec_t codec;
+
+    size_t block_size;
+    pa_memchunk memchunk;
+
+    pa_usec_t delay;
+    pa_usec_t start;
+    pa_smoother *smoother;
+    uint64_t write_count;
+};
+
+enum {
+    PA_SINK_MESSAGE_SET_RAOP_STATE = PA_SINK_MESSAGE_MAX
+};
+
+static void userdata_free(struct userdata *u);
+
+static void raop_state_cb(pa_raop_state_t state, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("State change recieved, informing IO thread...");
+
+    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_SET_RAOP_STATE, PA_INT_TO_PTR(state), 0, NULL, NULL);
+}
+
+static pa_usec_t sink_get_latency(const struct userdata *u) {
+    pa_usec_t r, now;
+    int64_t latency;
+
+    pa_assert(u);
+    pa_assert(u->smoother);
+
+    now = pa_rtclock_now();
+    now = pa_smoother_get(u->smoother, now);
+
+    latency = pa_bytes_to_usec(u->write_count, &u->sink->sample_spec) - (int64_t) now;
+    r = latency >= 0 ? (pa_usec_t) latency : 0;
+
+    return r;
+}
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    pa_assert(u);
+    pa_assert(u->raop);
+
+    switch (code) {
+        case PA_SINK_MESSAGE_SET_STATE: {
+            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+                case PA_SINK_SUSPENDED: {
+                    pa_log_debug("RAOP: SUSPENDED");
+
+                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+
+                    pa_smoother_pause(u->smoother, pa_rtclock_now());
+                    /* Issue a TEARDOWN if we are still connected */
+                    if (pa_raop_client_is_alive(u->raop)) {
+                        pa_raop_client_teardown(u->raop);
+                    }
+
+                    break;
+                }
+
+                case PA_SINK_IDLE: {
+                    pa_log_debug("RAOP: IDLE");
+
+                    /* Issue a FLUSH if we're comming from running state */
+                    if (u->sink->thread_info.state == PA_SINK_RUNNING) {
+                        pa_rtpoll_set_timer_disabled(u->rtpoll);
+                        pa_raop_client_flush(u->raop);
+                    }
+
+                    break;
+                }
+
+                case PA_SINK_RUNNING: {
+                    pa_usec_t now;
+
+                    pa_log_debug("RAOP: RUNNING");
+
+                    now = pa_rtclock_now();
+                    pa_smoother_resume(u->smoother, now, true);
+
+                    if (!pa_raop_client_is_alive(u->raop)) {
+                        /* Connecting will trigger a RECORD and start steaming */
+                        pa_raop_client_announce(u->raop);
+                    } else if (!pa_raop_client_can_stream(u->raop)) {
+                        /* RECORD alredy sent, simply start streaming */
+                        pa_raop_client_stream(u->raop);
+                        pa_rtpoll_set_timer_absolute(u->rtpoll, now);
+                        u->write_count = 0;
+                        u->start = now;
+                    }
+
+                    break;
+                }
+
+                case PA_SINK_UNLINKED:
+                case PA_SINK_INIT:
+                case PA_SINK_INVALID_STATE:
+                    break;
+            }
+
+            break;
+        }
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t r = 0;
+
+            if (pa_raop_client_can_stream(u->raop))
+                r = sink_get_latency(u);
+
+            *((pa_usec_t*) data) = r;
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_SET_RAOP_STATE: {
+            switch ((pa_raop_state_t) PA_PTR_TO_UINT(data)) {
+                case PA_RAOP_AUTHENTICATED: {
+                    if (!pa_raop_client_is_authenticated(u->raop)) {
+                        pa_module_unload_request(u->module, true);
+                    }
+
+                    return 0;
+                }
+
+                case PA_RAOP_CONNECTED: {
+                    pa_assert(!u->rtpoll_item);
+
+                    u->oob = pa_raop_client_register_pollfd(u->raop, u->rtpoll, &u->rtpoll_item);
+
+                    return 0;
+                }
+
+                case PA_RAOP_RECORDING: {
+                    pa_usec_t now;
+
+                    now = pa_rtclock_now();
+                    pa_rtpoll_set_timer_absolute(u->rtpoll, now);
+                    u->write_count = 0;
+                    u->start = now;
+
+                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+                        /* Our stream has been suspended so we just flush it... */
+                        pa_rtpoll_set_timer_disabled(u->rtpoll);
+                        pa_raop_client_flush(u->raop);
+                    }
+
+                    return 0;
+                }
+
+                case PA_RAOP_INVALID_STATE:
+                case PA_RAOP_DISCONNECTED: {
+                    unsigned int nbfds = 0;
+                    struct pollfd *pollfd;
+                    unsigned int i;
+
+                    if (u->rtpoll_item) {
+                        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, &nbfds);
+                        for (i = 0; i < nbfds; i++) {
+                            if (pollfd && pollfd->fd > 0)
+                               pa_close(pollfd->fd);
+                            pollfd++;
+                        }
+                        pa_rtpoll_item_free(u->rtpoll_item);
+                        u->rtpoll_item = NULL;
+                    }
+
+                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
+                        pa_rtpoll_set_timer_disabled(u->rtpoll);
+                    else
+                        pa_module_unload_request(u->module, true);
+
+                    return 0;
+                }
+            }
+
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void sink_set_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    pa_cvolume hw;
+    pa_volume_t v, v_orig;
+    char t[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+    pa_assert(u);
+
+    /* If we're muted we don't need to do anything. */
+    if (s->muted)
+        return;
+
+    /* Calculate the max volume of all channels.
+     * We'll use this as our (single) volume on the APEX device and emulate
+     * any variation in channel volumes in software. */
+    v = pa_cvolume_max(&s->real_volume);
+
+    v_orig = v;
+    v = pa_raop_client_adjust_volume(u->raop, v_orig);
+
+    pa_log_debug("Volume adjusted: orig=%u adjusted=%u", v_orig, v);
+
+    /* Create a pa_cvolume version of our single value. */
+    pa_cvolume_set(&hw, s->sample_spec.channels, v);
+
+    /* Perform any software manipulation of the volume needed. */
+    pa_sw_cvolume_divide(&s->soft_volume, &s->real_volume, &hw);
+
+    pa_log_debug("Requested volume: %s", pa_cvolume_snprint_verbose(t, sizeof(t), &s->real_volume, &s->channel_map, false));
+    pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint_verbose(t, sizeof(t), &hw, &s->channel_map, false));
+    pa_log_debug("Calculated software volume: %s",
+                 pa_cvolume_snprint_verbose(t, sizeof(t), &s->soft_volume, &s->channel_map, true));
+
+    /* Any necessary software volume manipulation is done so set
+     * our hw volume (or v as a single value) on the device. */
+    pa_raop_client_set_volume(u->raop, v);
+}
+
+static void sink_set_mute_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(u);
+    pa_assert(u->raop);
+
+    if (s->muted) {
+        pa_raop_client_set_volume(u->raop, PA_VOLUME_MUTED);
+    } else {
+        sink_set_volume_cb(s);
+    }
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    size_t offset = 0;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
+
+    for (;;) {
+        struct pollfd *pollfd = NULL;
+        unsigned int i, nbfds = 0;
+        pa_usec_t now, estimated, intvl;
+        uint64_t position;
+        ssize_t written;
+        size_t index;
+        int ret;
+
+        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+            if (u->sink->thread_info.rewind_requested)
+                pa_sink_process_rewind(u->sink, 0);
+        }
+
+        /* Polling (audio data + control socket + timing socket). */
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+        else if (ret == 0)
+            goto finish;
+
+        if (u->rtpoll_item) {
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, &nbfds);
+            /* If !oob: streaming driven by pollds (POLLOUT) */
+            if (pollfd && !u->oob && !pollfd->revents) {
+                for (i = 0; i < nbfds; i++) {
+                    pollfd->events = POLLOUT;
+                    pollfd->revents = 0;
+
+                    pollfd++;
+                }
+
+                continue;
+            }
+
+            /* if oob: streaming managed by timing, pollfd for oob sockets */
+            if (pollfd && u->oob && !pa_rtpoll_timer_elapsed(u->rtpoll)) {
+                uint8_t packet[32];
+                ssize_t read;
+
+                for (i = 0; i < nbfds; i++) {
+                    if (pollfd->revents & pollfd->events) {
+                        pollfd->revents = 0;
+                        read = pa_read(pollfd->fd, packet, sizeof(packet), NULL);
+                        pa_raop_client_handle_oob_packet(u->raop, pollfd->fd, packet, read);
+                    }
+
+                    pollfd++;
+                }
+
+                continue;
+            }
+        }
+
+        if (u->sink->thread_info.state != PA_SINK_RUNNING)
+            continue;
+        if (!pa_raop_client_can_stream(u->raop))
+            continue;
+
+        if (u->memchunk.length <= 0) {
+            if (u->memchunk.memblock)
+                pa_memblock_unref(u->memchunk.memblock);
+            pa_memchunk_reset(&u->memchunk);
+
+            /* Grab unencoded audio data from PulseAudio */
+            pa_sink_render_full(u->sink, u->block_size, &u->memchunk);
+            offset = u->memchunk.index;
+        }
+
+        pa_assert(u->memchunk.length > 0);
+
+        index = u->memchunk.index;
+        written = pa_raop_client_send_audio_packet(u->raop, &u->memchunk, offset);
+        if (written < 0) {
+            if (errno == EINTR) {
+                /* Just try again. */
+                pa_log_debug("Failed to write data to FIFO (EINTR), retrying");
+                goto fail;
+            } else if (errno != EAGAIN) {
+                /* Buffer is full, wait for POLLOUT. */
+                pollfd->events = POLLOUT;
+                pollfd->revents = 0;
+            } else {
+                pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+                goto fail;
+            }
+        } else {
+            u->write_count += (uint64_t) u->memchunk.index - (uint64_t) index;
+            position = u->write_count - pa_usec_to_bytes(u->delay, &u->sink->sample_spec);
+
+            now = pa_rtclock_now();
+            estimated = pa_bytes_to_usec(position, &u->sink->sample_spec);
+            pa_smoother_put(u->smoother, now, estimated);
+
+            if (u->oob && !pollfd->revents) {
+                /* Sleep until next packet transmission */
+                intvl = u->start + pa_bytes_to_usec(u->write_count, &u->sink->sample_spec);
+                pa_rtpoll_set_timer_absolute(u->rtpoll, intvl);
+            } else if (!u->oob) {
+                if (u->memchunk.length > 0) {
+                    pollfd->events = POLLOUT;
+                    pollfd->revents = 0;
+                } else {
+                    intvl = u->start + pa_bytes_to_usec(u->write_count, &u->sink->sample_spec);
+                    pa_rtpoll_set_timer_absolute(u->rtpoll, intvl);
+                    pollfd->revents = 0;
+                    pollfd->events = 0;
+                }
+            }
+        }
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+pa_sink* pa_raop_sink_new(pa_module *m, pa_modargs *ma, const char *driver) {
+    struct userdata *u = NULL;
+    pa_sample_spec ss;
+    char *thread_name = NULL;
+    const char *server, *protocol, *encryption, *codec;
+    const char /* *username, */ *password;
+    pa_sink_new_data data;
+    const char *name = NULL;
+    char * nick = NULL;
+
+    pa_assert(m);
+    pa_assert(ma);
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+        pa_log("Failed to parse sample specification");
+        goto fail;
+    }
+
+    if (!(server = pa_modargs_get_value(ma, "server", NULL))) {
+        pa_log("Failed to parse server argument");
+        goto fail;
+    }
+
+    if (!(protocol = pa_modargs_get_value(ma, "protocol", NULL))) {
+        pa_log("Failed to parse protocol argument");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->thread = NULL;
+    u->rtpoll = pa_rtpoll_new();
+    u->rtpoll_item = NULL;
+
+    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
+        pa_log("pa_thread_mq_init() failed.");
+        goto fail;
+    }
+
+    u->oob = true;
+
+    u->block_size = 0;
+    pa_memchunk_reset(&u->memchunk);
+
+    u->delay = 0;
+    u->smoother = pa_smoother_new(
+            PA_USEC_PER_SEC,
+            PA_USEC_PER_SEC*2,
+            true,
+            true,
+            10,
+            0,
+            false);
+    u->write_count = 0;
+
+    if (pa_streq(protocol, "TCP")) {
+        u->protocol = PA_RAOP_PROTOCOL_TCP;
+    } else if (pa_streq(protocol, "UDP")) {
+        u->protocol = PA_RAOP_PROTOCOL_UDP;
+    } else {
+        pa_log("Unsupported transport protocol argument: %s", protocol);
+        goto fail;
+    }
+
+    encryption = pa_modargs_get_value(ma, "encryption", NULL);
+    codec = pa_modargs_get_value(ma, "codec", NULL);
+
+    if (!encryption) {
+        u->encryption = PA_RAOP_ENCRYPTION_NONE;
+    } else if (pa_streq(encryption, "none")) {
+        u->encryption = PA_RAOP_ENCRYPTION_NONE;
+    } else if (pa_streq(encryption, "RSA")) {
+        u->encryption = PA_RAOP_ENCRYPTION_RSA;
+    } else {
+        pa_log("Unsupported encryption type argument: %s", encryption);
+        goto fail;
+    }
+
+    if (!codec) {
+        u->codec = PA_RAOP_CODEC_PCM;
+    } else if (pa_streq(codec, "PCM")) {
+        u->codec = PA_RAOP_CODEC_PCM;
+    } else if (pa_streq(codec, "ALAC")) {
+        u->codec = PA_RAOP_CODEC_ALAC;
+    } else {
+        pa_log("Unsupported audio codec argument: %s", codec);
+        goto fail;
+    }
+
+    pa_sink_new_data_init(&data);
+    data.driver = driver;
+    data.module = m;
+
+    if ((name = pa_modargs_get_value(ma, "sink_name", NULL))) {
+        pa_sink_new_data_set_name(&data, name);
+    } else {
+        if ((name = pa_modargs_get_value(ma, "name", NULL)))
+            nick = pa_sprintf_malloc("raop_client.%s", name);
+        if (!nick)
+            nick = pa_sprintf_malloc("raop_client.%s", server);
+        pa_sink_new_data_set_name(&data, nick);
+        pa_xfree(nick);
+    }
+
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "music");
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "RAOP sink '%s'", server);
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&data);
+        goto fail;
+    }
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY | PA_SINK_NETWORK);
+    pa_sink_new_data_done(&data);
+
+    if (!(u->sink)) {
+        pa_log("Failed to create sink object");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
+    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+    u->raop = pa_raop_client_new(u->core, server, u->protocol, u->encryption, u->codec);
+
+    if (!(u->raop)) {
+        pa_log("Failed to create RAOP client object");
+        goto fail;
+    }
+
+    /* The number of frames per blocks is not negotiable... */
+    pa_raop_client_get_frames_per_block(u->raop, &u->block_size);
+    u->block_size *= pa_frame_size(&ss);
+    pa_sink_set_max_request(u->sink, u->block_size);
+
+    pa_raop_client_set_state_callback(u->raop, raop_state_cb, u);
+
+    thread_name = pa_sprintf_malloc("raop-sink-%s", server);
+    if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) {
+        pa_log("Failed to create sink thread");
+        goto fail;
+    }
+    pa_xfree(thread_name);
+    thread_name = NULL;
+
+    pa_sink_put(u->sink);
+
+    /* username = pa_modargs_get_value(ma, "username", NULL); */
+    password = pa_modargs_get_value(ma, "password", NULL);
+    pa_raop_client_authenticate(u->raop, password );
+
+    return u->sink;
+
+fail:
+    pa_xfree(thread_name);
+    pa_xfree(nick);
+
+    if (u)
+        userdata_free(u);
+
+    return NULL;
+}
+
+static void userdata_free(struct userdata *u) {
+    pa_assert(u);
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+    u->sink = NULL;
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+    u->rtpoll_item = NULL;
+    u->rtpoll = NULL;
+
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->raop)
+        pa_raop_client_free(u->raop);
+    u->raop = NULL;
+
+    if (u->smoother)
+        pa_smoother_free(u->smoother);
+    u->smoother = NULL;
+
+    pa_xfree(u);
+}
+
+void pa_raop_sink_free(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    userdata_free(u);
+}
diff --git a/src/modules/raop/raop-sink.h b/src/modules/raop/raop-sink.h
new file mode 100644
index 0000000..dfa2f0c
--- /dev/null
+++ b/src/modules/raop/raop-sink.h
@@ -0,0 +1,33 @@
+#ifndef fooraopsinkfoo
+#define fooraopsinkfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio 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 Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/sink.h>
+
+pa_sink* pa_raop_sink_new(pa_module *m, pa_modargs *ma, const char *driver);
+
+void pa_raop_sink_free(pa_sink *s);
+
+#endif
diff --git a/src/modules/raop/raop-util.c b/src/modules/raop/raop-util.c
new file mode 100644
index 0000000..3e3eb00
--- /dev/null
+++ b/src/modules/raop/raop-util.c
@@ -0,0 +1,210 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Martin Blanchard
+  Copyright Kungliga Tekniska Høgskolan & Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio 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 Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/***
+  The base64 implementation was originally inspired by a file developed
+  by Kungliga Tekniska högskolan.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/err.h>
+#include <openssl/md5.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "raop-util.h"
+
+#ifndef MD5_DIGEST_LENGTH
+#define MD5_DIGEST_LENGTH 16
+#endif
+
+#define MD5_HASH_LENGTH (2*MD5_DIGEST_LENGTH)
+
+#define BASE64_DECODE_ERROR 0xffffffff
+
+static const char base64_chars[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int char_position(char c) {
+    if (c >= 'A' && c <= 'Z')
+        return c - 'A' + 0;
+    if (c >= 'a' && c <= 'z')
+        return c - 'a' + 26;
+    if (c >= '0' && c <= '9')
+        return c - '0' + 52;
+    if (c == '+')
+        return 62;
+    if (c == '/')
+        return 63;
+
+    return -1;
+}
+
+static unsigned int token_decode(const char *token) {
+    unsigned int val = 0;
+    int marker = 0;
+    int i;
+
+    if (strlen(token) < 4)
+        return BASE64_DECODE_ERROR;
+    for (i = 0; i < 4; i++) {
+        val *= 64;
+        if (token[i] == '=')
+            marker++;
+        else if (marker > 0)
+            return BASE64_DECODE_ERROR;
+        else {
+            int lpos = char_position(token[i]);
+            if (lpos < 0)
+                return BASE64_DECODE_ERROR;
+            val += lpos;
+        }
+    }
+
+    if (marker > 2)
+        return BASE64_DECODE_ERROR;
+
+    return (marker << 24) | val;
+}
+
+int pa_raop_base64_encode(const void *data, int len, char **str) {
+    const unsigned char *q;
+    char *p, *s = NULL;
+    int i, c;
+
+    pa_assert(data);
+    pa_assert(str);
+
+    p = s = pa_xnew(char, len * 4 / 3 + 4);
+    q = (const unsigned char *) data;
+    for (i = 0; i < len;) {
+        c = q[i++];
+        c *= 256;
+        if (i < len)
+            c += q[i];
+        i++;
+        c *= 256;
+        if (i < len)
+            c += q[i];
+        i++;
+        p[0] = base64_chars[(c & 0x00fc0000) >> 18];
+        p[1] = base64_chars[(c & 0x0003f000) >> 12];
+        p[2] = base64_chars[(c & 0x00000fc0) >> 6];
+        p[3] = base64_chars[(c & 0x0000003f) >> 0];
+        if (i > len)
+            p[3] = '=';
+        if (i > len + 1)
+            p[2] = '=';
+        p += 4;
+    }
+
+    *p = 0;
+    *str = s;
+    return strlen(s);
+}
+
+int pa_raop_base64_decode(const char *str, void *data) {
+    const char *p;
+    unsigned char *q;
+
+    pa_assert(str);
+    pa_assert(data);
+
+    q = data;
+    for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
+        unsigned int val = token_decode(p);
+        unsigned int marker = (val >> 24) & 0xff;
+        if (val == BASE64_DECODE_ERROR)
+            return -1;
+        *q++ = (val >> 16) & 0xff;
+        if (marker < 2)
+            *q++ = (val >> 8) & 0xff;
+        if (marker < 1)
+            *q++ = val & 0xff;
+    }
+
+    return q - (unsigned char *) data;
+}
+
+int pa_raop_md5_hash(const char *data, int len, char **str) {
+    unsigned char d[MD5_DIGEST_LENGTH];
+    char *s = NULL;
+    int i;
+
+    pa_assert(data);
+    pa_assert(str);
+
+    MD5((unsigned char*) data, len, d);
+    s = pa_xnew(char, MD5_HASH_LENGTH);
+    for (i = 0; i < MD5_DIGEST_LENGTH; i++)
+        sprintf(&s[2*i], "%02x", (unsigned int) d[i]);
+
+    *str = s;
+    s[MD5_HASH_LENGTH] = 0;
+    return strlen(s);
+}
+
+int pa_raop_basic_response(const char *user, const char *pwd, char **str) {
+    char *tmp, *B = NULL;
+
+    pa_assert(str);
+
+    tmp = pa_sprintf_malloc("%s:%s", user, pwd);
+    pa_raop_base64_encode(tmp, strlen(tmp), &B);
+    pa_xfree(tmp);
+
+    *str = B;
+    return strlen(B);
+}
+
+int pa_raop_digest_response(const char *user, const char *realm, const char *password,
+                            const char *nonce, const char *uri, char **str) {
+    char *A1, *HA1, *A2, *HA2;
+    char *tmp, *KD = NULL;
+
+    pa_assert(str);
+
+    A1 = pa_sprintf_malloc("%s:%s:%s", user, realm, password);
+    pa_raop_md5_hash(A1, strlen(A1), &HA1);
+    pa_xfree(A1);
+
+    A2 = pa_sprintf_malloc("OPTIONS:%s", uri);
+    pa_raop_md5_hash(A2, strlen(A2), &HA2);
+    pa_xfree(A2);
+
+    tmp = pa_sprintf_malloc("%s:%s:%s", HA1, nonce, HA2);
+    pa_raop_md5_hash(tmp, strlen(tmp), &KD);
+    pa_xfree(tmp);
+
+    pa_xfree(HA1);
+    pa_xfree(HA2);
+
+    *str = KD;
+    return strlen(KD);
+}
diff --git a/src/modules/raop/raop-util.h b/src/modules/raop/raop-util.h
new file mode 100644
index 0000000..d3f7566
--- /dev/null
+++ b/src/modules/raop/raop-util.h
@@ -0,0 +1,39 @@
+#ifndef fooraoputilfoo
+#define fooraoputilfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Colin Guthrie
+  Copyright Kungliga Tekniska högskolan
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio 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 Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/***
+  This file was originally inspired by a file developed by
+  Kungliga Tekniska högskolan.
+***/
+
+int pa_raop_base64_encode(const void *data, int len, char **str);
+int pa_raop_base64_decode(const char *str, void *data);
+
+int pa_raop_md5_hash(const char *data, int len, char **str);
+
+int pa_raop_basic_response(const char *user, const char *pwd, char **str);
+int pa_raop_digest_response(const char *user, const char *realm, const char *password,
+                            const char *nonce, const char *uri, char **str);
+
+#endif
diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
deleted file mode 100644
index 1323cd0..0000000
--- a/src/modules/raop/raop_client.c
+++ /dev/null
@@ -1,1722 +0,0 @@
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2008 Colin Guthrie
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  PulseAudio 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 Lesser General Public License
-  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/ioctl.h>
-#include <math.h>
-
-#ifdef HAVE_SYS_FILIO_H
-#include <sys/filio.h>
-#endif
-
-#include <pulse/xmalloc.h>
-#include <pulse/timeval.h>
-#include <pulse/sample.h>
-
-#include <pulsecore/core-error.h>
-#include <pulsecore/core-rtclock.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/iochannel.h>
-#include <pulsecore/arpa-inet.h>
-#include <pulsecore/socket-util.h>
-#include <pulsecore/log.h>
-#include <pulsecore/parseaddr.h>
-#include <pulsecore/macro.h>
-#include <pulsecore/memchunk.h>
-#include <pulsecore/random.h>
-
-#include "raop_client.h"
-
-#include "rtsp_client.h"
-#include "raop_packet_buffer.h"
-#include "raop_crypto.h"
-#include "raop_util.h"
-
-#define JACK_STATUS_DISCONNECTED 0
-#define JACK_STATUS_CONNECTED 1
-
-#define JACK_TYPE_ANALOG 0
-#define JACK_TYPE_DIGITAL 1
-
-#define VOLUME_DEF -30
-#define VOLUME_MIN -144
-#define VOLUME_MAX 0
-
-#define USER_AGENT "iTunes/11.0.4 (Windows; N)"
-#define USER_NAME "iTunes"
-
-#define DEFAULT_RAOP_PORT 5000
-#define UDP_DEFAULT_AUDIO_PORT 6000
-#define UDP_DEFAULT_CONTROL_PORT 6001
-#define UDP_DEFAULT_TIMING_PORT 6002
-
-#define UDP_DEFAULT_PKT_BUF_SIZE 1000
-
-typedef enum {
-    UDP_PAYLOAD_TIMING_REQUEST = 0x52,
-    UDP_PAYLOAD_TIMING_RESPONSE = 0x53,
-    UDP_PAYLOAD_SYNCHRONIZATION = 0x54,
-    UDP_PAYLOAD_RETRANSMIT_REQUEST = 0x55,
-    UDP_PAYLOAD_RETRANSMIT_REPLY = 0x56,
-    UDP_PAYLOAD_AUDIO_DATA = 0x60
-} pa_raop_udp_payload_type;
-
-struct pa_raop_client {
-    pa_core *core;
-    char *host;
-    uint16_t port;
-    pa_rtsp_client *rtsp;
-    char *sci, *sid;
-    char *pwd;
-
-    uint8_t jack_type;
-    uint8_t jack_status;
-
-    pa_raop_protocol_t protocol;
-
-    int encryption; /* Enable encryption? */
-    pa_raop_secret *aes;
-
-    uint16_t seq;
-    uint32_t rtptime;
-
-    /* Members only for the TCP protocol */
-    pa_socket_client *tcp_sc;
-    int tcp_fd;
-
-    pa_raop_client_cb_t tcp_callback;
-    void *tcp_userdata;
-    pa_raop_client_closed_cb_t tcp_closed_callback;
-    void *tcp_closed_userdata;
-
-    /* Members only for the UDP protocol */
-    uint16_t udp_my_control_port;
-    uint16_t udp_my_timing_port;
-    uint16_t udp_server_control_port;
-    uint16_t udp_server_timing_port;
-
-    int udp_stream_fd;
-    int udp_control_fd;
-    int udp_timing_fd;
-
-    uint32_t udp_ssrc;
-
-    bool is_recording;
-
-    bool udp_first_packet;
-    uint32_t udp_sync_interval;
-    uint32_t udp_sync_count;
-
-    pa_raop_client_auth_cb_t udp_auth_callback;
-    void *udp_auth_userdata;
-
-    pa_raop_client_setup_cb_t udp_setup_callback;
-    void *udp_setup_userdata;
-
-    pa_raop_client_record_cb_t udp_record_callback;
-    void *udp_record_userdata;
-
-    pa_raop_client_disconnected_cb_t udp_disconnected_callback;
-    void *udp_disconnected_userdata;
-
-    pa_raop_packet_buffer *packet_buffer;
-};
-
-/* Timming packet header (8x8):
- *  [0]   RTP v2: 0x80,
- *  [1]   Payload type: 0x53 | marker bit: 0x80,
- *  [2,3] Sequence number: 0x0007,
- *  [4,7] Timestamp: 0x00000000 (unused). */
-static const uint8_t udp_timming_header[8] = {
-    0x80, 0xd3, 0x00, 0x07,
-    0x00, 0x00, 0x00, 0x00
-};
-
-/* Sync packet header (8x8):
- *  [0]   RTP v2: 0x80,
- *  [1]   Payload type: 0x54 | marker bit: 0x80,
- *  [2,3] Sequence number: 0x0007,
- *  [4,7] Timestamp: 0x00000000 (to be set). */
-static const uint8_t udp_sync_header[8] = {
-    0x80, 0xd4, 0x00, 0x07,
-    0x00, 0x00, 0x00, 0x00
-};
-
-static const uint8_t tcp_audio_header[16] = {
-    0x24, 0x00, 0x00, 0x00,
-    0xF0, 0xFF, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00,
-};
-
-/* Audio packet header (12x8):
- *  [0]    RTP v2: 0x80,
- *  [1]    Payload type: 0x60,
- *  [2,3]  Sequence number: 0x0000 (to be set),
- *  [4,7]  Timestamp: 0x00000000 (to be set),
- *  [8,12] SSRC: 0x00000000 (to be set).*/
-static const uint8_t udp_audio_header[12] = {
-    0x80, 0x60, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00
-};
-
-/**
- * Function to write bits into a buffer.
- * @param buffer Handle to the buffer. It will be incremented if new data requires it.
- * @param bit_pos A pointer to a position buffer to keep track the current write location (0 for MSB, 7 for LSB)
- * @param size A pointer to the byte size currently written. This allows the calling function to do simple buffer overflow checks
- * @param data The data to write
- * @param data_bit_len The number of bits from data to write
- */
-static inline void bit_writer(uint8_t **buffer, uint8_t *bit_pos, int *size, uint8_t data, uint8_t data_bit_len) {
-    int bits_left, bit_overflow;
-    uint8_t bit_data;
-
-    if (!data_bit_len)
-        return;
-
-    /* If bit pos is zero, we will definately use at least one bit from the current byte so size increments. */
-    if (!*bit_pos)
-        *size += 1;
-
-    /* Calc the number of bits left in the current byte of buffer. */
-    bits_left = 7 - *bit_pos  + 1;
-    /* Calc the overflow of bits in relation to how much space we have left... */
-    bit_overflow = bits_left - data_bit_len;
-    if (bit_overflow >= 0) {
-        /* We can fit the new data in our current byte.
-         * As we write from MSB->LSB we need to left shift by the overflow amount. */
-        bit_data = data << bit_overflow;
-        if (*bit_pos)
-            **buffer |= bit_data;
-        else
-            **buffer = bit_data;
-        /* If our data fits exactly into the current byte, we need to increment our pointer. */
-        if (0 == bit_overflow) {
-            /* Do not increment size as it will be incremented on next call as bit_pos is zero. */
-            *buffer += 1;
-            *bit_pos = 0;
-        } else {
-            *bit_pos += data_bit_len;
-        }
-    } else {
-        /* bit_overflow is negative, there for we will need a new byte from our buffer
-         * Firstly fill up what's left in the current byte. */
-        bit_data = data >> -bit_overflow;
-        **buffer |= bit_data;
-        /* Increment our buffer pointer and size counter. */
-        *buffer += 1;
-        *size += 1;
-        **buffer = data << (8 + bit_overflow);
-        *bit_pos = -bit_overflow;
-    }
-}
-
-static inline void rtrimchar(char *str, char rc) {
-    char *sp = str + strlen(str) - 1;
-    while (sp >= str && *sp == rc) {
-        *sp = '\0';
-        sp -= 1;
-    }
-}
-
-static void tcp_on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
-    pa_raop_client *c = userdata;
-
-    pa_assert(sc);
-    pa_assert(c);
-    pa_assert(c->tcp_sc == sc);
-    pa_assert(c->tcp_fd < 0);
-    pa_assert(c->tcp_callback);
-
-    pa_socket_client_unref(c->tcp_sc);
-    c->tcp_sc = NULL;
-
-    if (!io) {
-        pa_log("Connection failed: %s", pa_cstrerror(errno));
-        return;
-    }
-
-    c->tcp_fd = pa_iochannel_get_send_fd(io);
-
-    pa_iochannel_set_noclose(io, true);
-    pa_iochannel_free(io);
-
-    pa_make_tcp_socket_low_delay(c->tcp_fd);
-
-    pa_log_debug("Connection established");
-    c->tcp_callback(c->tcp_fd, c->tcp_userdata);
-}
-
-static inline uint64_t timeval_to_ntp(struct timeval *tv) {
-    uint64_t ntp = 0;
-
-    /* Converting micro seconds to a fraction. */
-    ntp = (uint64_t) tv->tv_usec * UINT32_MAX / PA_USEC_PER_SEC;
-    /* Moving reference from  1 Jan 1970 to 1 Jan 1900 (seconds). */
-    ntp |= (uint64_t) (tv->tv_sec + 0x83aa7e80) << 32;
-
-    return ntp;
-}
-
-static int connect_udp_socket(pa_raop_client *c, int fd, uint16_t port) {
-    struct sockaddr_in sa4;
-#ifdef HAVE_IPV6
-    struct sockaddr_in6 sa6;
-#endif
-    struct sockaddr *sa;
-    socklen_t salen;
-    sa_family_t af;
-
-    pa_zero(sa4);
-#ifdef HAVE_IPV6
-    pa_zero(sa6);
-#endif
-    if (inet_pton(AF_INET, c->host, &sa4.sin_addr) > 0) {
-        sa4.sin_family = af = AF_INET;
-        sa4.sin_port = htons(port);
-        sa = (struct sockaddr *) &sa4;
-        salen = sizeof(sa4);
-#ifdef HAVE_IPV6
-    } else if (inet_pton(AF_INET6, c->host, &sa6.sin6_addr) > 0) {
-        sa6.sin6_family = af = AF_INET6;
-        sa6.sin6_port = htons(port);
-        sa = (struct sockaddr *) &sa6;
-        salen = sizeof(sa6);
-#endif
-    } else {
-        pa_log("Invalid destination '%s'", c->host);
-        goto fail;
-    }
-
-    if (fd < 0 && (fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) {
-        pa_log("socket() failed: %s", pa_cstrerror(errno));
-        goto fail;
-    }
-
-    /* If the socket queue is full, let's drop packets */
-    pa_make_udp_socket_low_delay(fd);
-    pa_make_fd_nonblock(fd);
-
-    if (connect(fd, sa, salen) < 0) {
-        pa_log("connect() failed: %s", pa_cstrerror(errno));
-        goto fail;
-    }
-
-    pa_log_debug("Connected to %s on port %d (SOCK_DGRAM)", c->host, port);
-    return fd;
-
-fail:
-    if (fd >= 0)
-        pa_close(fd);
-
-    return -1;
-}
-
-static int open_bind_udp_socket(pa_raop_client *c, uint16_t *actual_port) {
-    int fd = -1;
-    uint16_t port;
-    struct sockaddr_in sa4;
-#ifdef HAVE_IPV6
-    struct sockaddr_in6 sa6;
-#endif
-    struct sockaddr *sa;
-    uint16_t *sa_port;
-    socklen_t salen;
-    sa_family_t af;
-    int one = 1;
-
-    pa_assert(actual_port);
-
-    port = *actual_port;
-
-    pa_zero(sa4);
-#ifdef HAVE_IPV6
-    pa_zero(sa6);
-#endif
-    if (inet_pton(AF_INET, pa_rtsp_localip(c->rtsp), &sa4.sin_addr) > 0) {
-        sa4.sin_family = af = AF_INET;
-        sa4.sin_port = htons(port);
-        sa = (struct sockaddr *) &sa4;
-        salen = sizeof(sa4);
-        sa_port = &sa4.sin_port;
-#ifdef HAVE_IPV6
-    } else if (inet_pton(AF_INET6, pa_rtsp_localip(c->rtsp), &sa6.sin6_addr) > 0) {
-        sa6.sin6_family = af = AF_INET6;
-        sa6.sin6_port = htons(port);
-        sa = (struct sockaddr *) &sa6;
-        salen = sizeof(sa6);
-        sa_port = &sa6.sin6_port;
-#endif
-    } else {
-        pa_log("Could not determine which address family to use");
-        goto fail;
-    }
-
-    pa_zero(sa4);
-#ifdef HAVE_IPV6
-    pa_zero(sa6);
-#endif
-
-    if ((fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) {
-        pa_log("socket() failed: %s", pa_cstrerror(errno));
-        goto fail;
-    }
-
-#ifdef SO_TIMESTAMP
-    if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0) {
-        pa_log("setsockopt(SO_TIMESTAMP) failed: %s", pa_cstrerror(errno));
-        goto fail;
-    }
-#else
-    pa_log("SO_TIMESTAMP unsupported on this platform");
-    goto fail;
-#endif
-
-    one = 1;
-    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
-        pa_log("setsockopt(SO_REUSEADDR) failed: %s", pa_cstrerror(errno));
-        goto fail;
-    }
-
-    do {
-        *sa_port = htons(port);
-
-        if (bind(fd, sa, salen) < 0 && errno != EADDRINUSE) {
-            pa_log("bind_socket() failed: %s", pa_cstrerror(errno));
-            goto fail;
-        }
-        break;
-    } while (++port > 0);
-
-    pa_log_debug("Socket bound to port %d (SOCK_DGRAM)", port);
-    *actual_port = port;
-
-    return fd;
-
-fail:
-    if (fd >= 0)
-        pa_close(fd);
-
-    return -1;
-}
-
-static int udp_send_timing_packet(pa_raop_client *c, const uint32_t data[6], uint64_t received) {
-    uint32_t packet[8];
-    struct timeval tv;
-    ssize_t written = 0;
-    uint64_t trs = 0;
-    int rv = 1;
-
-    memcpy(packet, udp_timming_header, sizeof(udp_timming_header));
-    /* Copying originate timestamp from the incoming request packet. */
-    packet[2] = data[4];
-    packet[3] = data[5];
-    /* Set the receive timestamp to reception time. */
-    packet[4] = htonl(received >> 32);
-    packet[5] = htonl(received & 0xffffffff);
-    /* Set the transmit timestamp to current time. */
-    trs = timeval_to_ntp(pa_rtclock_get(&tv));
-    packet[6] = htonl(trs >> 32);
-    packet[7] = htonl(trs & 0xffffffff);
-
-    written = pa_loop_write(c->udp_timing_fd, packet, sizeof(packet), NULL);
-    if (written == sizeof(packet))
-        rv = 0;
-
-    return rv;
-}
-
-static int udp_send_sync_packet(pa_raop_client *c, uint32_t stamp) {
-    const uint32_t delay = 88200;
-    uint32_t packet[5];
-    struct timeval tv;
-    ssize_t written = 0;
-    uint64_t trs = 0;
-    int rv = 1;
-
-    memcpy(packet, udp_sync_header, sizeof(udp_sync_header));
-    if (c->udp_first_packet)
-        packet[0] |= 0x10;
-    stamp -= delay;
-    packet[1] = htonl(stamp);
-    /* Set the transmited timestamp to current time. */
-    trs = timeval_to_ntp(pa_rtclock_get(&tv));
-    packet[2] = htonl(trs >> 32);
-    packet[3] = htonl(trs & 0xffffffff);
-    stamp += delay;
-    packet[4] = htonl(stamp);
-
-    written = pa_loop_write(c->udp_control_fd, packet, sizeof(packet), NULL);
-    if (written == sizeof(packet))
-        rv = 0;
-
-    return rv;
-}
-
-static void udp_build_audio_header(pa_raop_client *c, uint32_t *buffer, size_t size) {
-    pa_assert(size >= sizeof(udp_audio_header));
-
-    memcpy(buffer, udp_audio_header, sizeof(udp_audio_header));
-    if (c->udp_first_packet)
-        buffer[0] |= htonl((uint32_t) 0x80 << 16);
-    buffer[0] |= htonl((uint32_t) c->seq);
-    buffer[1] = htonl(c->rtptime);
-    buffer[2] = htonl(c->udp_ssrc);
-}
-
-/* Audio retransmission header:
- * [0]    RTP v2: 0x80
- * [1]    Payload type: 0x56 + 0x80 (marker == on)
- * [2]    Unknown; seems always 0x01
- * [3]    Unknown; seems some random number around 0x20~0x40
- * [4,5]  Original RTP header htons(0x8060)
- * [6,7]  Packet sequence number to be retransmitted
- * [8,11] Original RTP timestamp on the lost packet */
-static void udp_build_retrans_header(uint32_t *buffer, size_t size, uint16_t seq_num) {
-    uint8_t x = 0x30; /* FIXME: what's this?? */
-
-    pa_assert(size >= sizeof(uint32_t) * 2);
-
-    buffer[0] = htonl((uint32_t) 0x80000000
-                      | ((uint32_t) UDP_PAYLOAD_RETRANSMIT_REPLY | 0x80) << 16
-                      | 0x0100
-                      | x);
-    buffer[1] = htonl((uint32_t) 0x80600000 | seq_num);
-}
-
-static ssize_t udp_send_audio_packet(pa_raop_client *c, bool retrans, uint8_t *buffer, size_t size) {
-    ssize_t length;
-    int fd = retrans ? c->udp_control_fd : c->udp_stream_fd;
-
-    length = pa_write(fd, buffer, size, NULL);
-    if (length < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
-        pa_log_debug("Discarding audio packet %d due to EAGAIN", c->seq);
-        length = size;
-    }
-    return length;
-}
-
-static void do_rtsp_announce(pa_raop_client *c) {
-    char *key, *iv, *sac = NULL, *sdp;
-    uint16_t rand_data;
-    const char *ip;
-    char *url;
-
-    ip = pa_rtsp_localip(c->rtsp);
-    /* First of all set the url properly. */
-    url = pa_sprintf_malloc("rtsp://%s/%s", ip, c->sid);
-    pa_rtsp_set_url(c->rtsp, url);
-    pa_xfree(url);
-
-    /* UDP protocol does not need "Apple-Challenge" at announce. */
-    if (c->protocol == RAOP_TCP) {
-        pa_random(&rand_data, sizeof(rand_data));
-        pa_raop_base64_encode(&rand_data, 8*sizeof(rand_data), &sac);
-        rtrimchar(sac, '=');
-        pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
-    }
-
-    if (c->encryption) {
-        iv = pa_raop_secret_get_iv(c->aes);
-        rtrimchar(iv, '=');
-        key = pa_raop_secret_get_key(c->aes);
-        rtrimchar(key, '=');
-
-        sdp = pa_sprintf_malloc(
-            "v=0\r\n"
-            "o=iTunes %s 0 IN IP4 %s\r\n"
-            "s=iTunes\r\n"
-            "c=IN IP4 %s\r\n"
-            "t=0 0\r\n"
-            "m=audio 0 RTP/AVP 96\r\n"
-            "a=rtpmap:96 AppleLossless\r\n"
-            "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 44100\r\n"
-            "a=rsaaeskey:%s\r\n"
-            "a=aesiv:%s\r\n",
-            c->sid, ip, c->host,
-            c->protocol == RAOP_TCP ? 4096 : UDP_FRAMES_PER_PACKET,
-            key, iv);
-
-        pa_xfree(iv);
-        pa_xfree(key);
-    } else {
-        sdp = pa_sprintf_malloc(
-            "v=0\r\n"
-            "o=iTunes %s 0 IN IP4 %s\r\n"
-            "s=iTunes\r\n"
-            "c=IN IP4 %s\r\n"
-            "t=0 0\r\n"
-            "m=audio 0 RTP/AVP 96\r\n"
-            "a=rtpmap:96 AppleLossless\r\n"
-            "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 44100\r\n",
-            c->sid, ip, c->host,
-            c->protocol == RAOP_TCP ? 4096 : UDP_FRAMES_PER_PACKET);
-    }
-
-    pa_rtsp_announce(c->rtsp, sdp);
-
-    pa_xfree(sac);
-    pa_xfree(sdp);
-}
-
-static void tcp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_rtsp_status status, pa_headerlist* headers, void *userdata) {
-    pa_raop_client* c = userdata;
-    pa_assert(c);
-    pa_assert(rtsp);
-    pa_assert(rtsp == c->rtsp);
-
-    switch (state) {
-        case STATE_CONNECT: {
-            pa_log_debug("RAOP: CONNECTED");
-            do_rtsp_announce(c);
-            break;
-        }
-
-        case STATE_OPTIONS:
-            pa_log_debug("RAOP: OPTIONS");
-            break;
-
-        case STATE_ANNOUNCE:
-            pa_log_debug("RAOP: ANNOUNCED");
-            pa_rtsp_remove_header(c->rtsp, "Apple-Challenge");
-            pa_rtsp_setup(c->rtsp, NULL);
-            break;
-
-        case STATE_SETUP: {
-            char *aj = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Jack-Status"));
-            pa_log_debug("RAOP: SETUP");
-            if (aj) {
-                char *token, *pc;
-                char delimiters[] = ";";
-                const char* token_state = NULL;
-                c->jack_type = JACK_TYPE_ANALOG;
-                c->jack_status = JACK_STATUS_DISCONNECTED;
-
-                while ((token = pa_split(aj, delimiters, &token_state))) {
-                    if ((pc = strstr(token, "="))) {
-                      *pc = 0;
-                      if (pa_streq(token, "type") && pa_streq(pc+1, "digital")) {
-                          c->jack_type = JACK_TYPE_DIGITAL;
-                      }
-                    } else {
-                        if (pa_streq(token, "connected"))
-                            c->jack_status = JACK_STATUS_CONNECTED;
-                    }
-                    pa_xfree(token);
-                }
-                pa_xfree(aj);
-            } else {
-                pa_log_warn("Audio Jack Status missing");
-            }
-            pa_rtsp_record(c->rtsp, &c->seq, &c->rtptime);
-            break;
-        }
-
-        case STATE_RECORD: {
-            uint32_t port = pa_rtsp_serverport(c->rtsp);
-            pa_log_debug("RAOP: RECORDED");
-
-            if (!(c->tcp_sc = pa_socket_client_new_string(c->core->mainloop, true, c->host, port))) {
-                pa_log("failed to connect to server '%s:%d'", c->host, port);
-                return;
-            }
-            pa_socket_client_set_callback(c->tcp_sc, tcp_on_connection, c);
-            break;
-        }
-
-        case STATE_FLUSH:
-            pa_log_debug("RAOP: FLUSHED");
-            break;
-
-        case STATE_TEARDOWN:
-            pa_log_debug("RAOP: TEARDOWN");
-            break;
-
-        case STATE_SET_PARAMETER:
-            pa_log_debug("RAOP: SET_PARAMETER");
-            break;
-
-        case STATE_DISCONNECTED:
-            pa_assert(c->tcp_closed_callback);
-            pa_assert(c->rtsp);
-
-            pa_log_debug("RTSP control channel closed");
-            pa_rtsp_client_free(c->rtsp);
-            c->rtsp = NULL;
-            if (c->tcp_fd > 0) {
-                /* We do not close the fd, we leave it to the closed callback to do that */
-                c->tcp_fd = -1;
-            }
-            if (c->tcp_sc) {
-                pa_socket_client_unref(c->tcp_sc);
-                c->tcp_sc = NULL;
-            }
-            pa_xfree(c->sid);
-            c->sid = NULL;
-            c->tcp_closed_callback(c->tcp_closed_userdata);
-            break;
-    }
-}
-
-static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_rtsp_status status, pa_headerlist *headers, void *userdata) {
-    pa_raop_client *c = userdata;
-
-    pa_assert(c);
-    pa_assert(rtsp);
-    pa_assert(rtsp == c->rtsp);
-    pa_assert(STATUS_OK == status);
-
-    switch (state) {
-        case STATE_CONNECT: {
-            uint16_t rand;
-            char *sac;
-
-            /* Set the Apple-Challenge key */
-            pa_random(&rand, sizeof(rand));
-            pa_raop_base64_encode(&rand, 8*sizeof(rand), &sac);
-            rtrimchar(sac, '=');
-            pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
-
-            pa_rtsp_options(c->rtsp);
-
-            pa_xfree(sac);
-            break;
-        }
-
-        case STATE_OPTIONS: {
-            pa_log_debug("RAOP: OPTIONS");
-
-            pa_rtsp_remove_header(c->rtsp, "Apple-Challenge");
-            do_rtsp_announce(c);
-            break;
-        }
-
-        case STATE_ANNOUNCE: {
-            char *trs;
-
-            pa_assert(c->udp_control_fd < 0);
-            pa_assert(c->udp_timing_fd < 0);
-
-            c->udp_control_fd = open_bind_udp_socket(c, &c->udp_my_control_port);
-            if (c->udp_control_fd < 0)
-                goto error_announce;
-            c->udp_timing_fd  = open_bind_udp_socket(c, &c->udp_my_timing_port);
-            if (c->udp_timing_fd < 0)
-                goto error_announce;
-
-            trs = pa_sprintf_malloc("RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=%d;timing_port=%d",
-                c->udp_my_control_port,
-                c->udp_my_timing_port);
-
-            pa_rtsp_setup(c->rtsp, trs);
-
-            pa_xfree(trs);
-            break;
-
-        error_announce:
-            if (c->udp_control_fd > 0) {
-                pa_close(c->udp_control_fd);
-                c->udp_control_fd = -1;
-            }
-            if (c->udp_timing_fd > 0) {
-                pa_close(c->udp_timing_fd);
-                c->udp_timing_fd = -1;
-            }
-
-            pa_rtsp_client_free(c->rtsp);
-            c->rtsp = NULL;
-
-            c->udp_my_control_port     = UDP_DEFAULT_CONTROL_PORT;
-            c->udp_server_control_port = UDP_DEFAULT_CONTROL_PORT;
-            c->udp_my_timing_port      = UDP_DEFAULT_TIMING_PORT;
-            c->udp_server_timing_port  = UDP_DEFAULT_TIMING_PORT;
-
-            pa_log_error("aborting RTSP announce, failed creating required sockets");
-        }
-
-        case STATE_SETUP: {
-            uint32_t stream_port = UDP_DEFAULT_AUDIO_PORT;
-            char *ajs, *trs, *token, *pc;
-            char delimiters[] = ";";
-            const char *token_state = NULL;
-            uint32_t port = 0;
-            int ret;
-
-            pa_log_debug("RAOP: SETUP");
-
-            ajs = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Jack-Status"));
-            trs = pa_xstrdup(pa_headerlist_gets(headers, "Transport"));
-
-            if (ajs) {
-                c->jack_type = JACK_TYPE_ANALOG;
-                c->jack_status = JACK_STATUS_DISCONNECTED;
-
-                while ((token = pa_split(ajs, delimiters, &token_state))) {
-                    if ((pc = strstr(token, "="))) {
-                      *pc = 0;
-                      if (pa_streq(token, "type") && pa_streq(pc + 1, "digital"))
-                          c->jack_type = JACK_TYPE_DIGITAL;
-                    } else {
-                        if (pa_streq(token, "connected"))
-                            c->jack_status = JACK_STATUS_CONNECTED;
-                    }
-                    pa_xfree(token);
-                }
-
-            } else {
-                pa_log_warn("Audio-Jack-Status missing");
-            }
-
-            token_state = NULL;
-
-            if (trs) {
-                /* Now parse out the server port component of the response. */
-                while ((token = pa_split(trs, delimiters, &token_state))) {
-                    if ((pc = strstr(token, "="))) {
-                        *pc = 0;
-                        if (pa_streq(token, "control_port")) {
-                            port = 0;
-                            pa_atou(pc + 1, &port);
-                            c->udp_server_control_port = port;
-                        }
-                        if (pa_streq(token, "timing_port")) {
-                            port = 0;
-                            pa_atou(pc + 1, &port);
-                            c->udp_server_timing_port = port;
-                        }
-                        *pc = '=';
-                    }
-                    pa_xfree(token);
-                }
-            } else {
-                pa_log_warn("Transport missing");
-            }
-
-            pa_xfree(ajs);
-            pa_xfree(trs);
-
-            stream_port = pa_rtsp_serverport(c->rtsp);
-            if (stream_port == 0)
-                goto error;
-            if (c->udp_server_control_port == 0 || c->udp_server_timing_port == 0)
-                goto error;
-
-            pa_log_debug("Using server_port=%d, control_port=%d & timing_port=%d",
-                stream_port,
-                c->udp_server_control_port,
-                c->udp_server_timing_port);
-
-            pa_assert(c->udp_stream_fd < 0);
-            pa_assert(c->udp_control_fd >= 0);
-            pa_assert(c->udp_timing_fd >= 0);
-
-            c->udp_stream_fd = connect_udp_socket(c, -1, stream_port);
-            if (c->udp_stream_fd <= 0)
-                goto error;
-            ret = connect_udp_socket(c, c->udp_control_fd,
-                                     c->udp_server_control_port);
-            if (ret < 0)
-                goto error;
-            ret = connect_udp_socket(c, c->udp_timing_fd,
-                                     c->udp_server_timing_port);
-            if (ret < 0)
-                goto error;
-
-            c->udp_setup_callback(c->udp_control_fd, c->udp_timing_fd, c->udp_setup_userdata);
-            pa_rtsp_record(c->rtsp, &c->seq, &c->rtptime);
-
-            break;
-
-        error:
-            if (c->udp_stream_fd > 0) {
-                pa_close(c->udp_stream_fd);
-                c->udp_stream_fd = -1;
-            }
-            if (c->udp_control_fd > 0) {
-                pa_close(c->udp_control_fd);
-                c->udp_control_fd = -1;
-            }
-            if (c->udp_timing_fd > 0) {
-                pa_close(c->udp_timing_fd);
-                c->udp_timing_fd = -1;
-            }
-
-            pa_rtsp_client_free(c->rtsp);
-            c->rtsp = NULL;
-
-            c->udp_my_control_port     = UDP_DEFAULT_CONTROL_PORT;
-            c->udp_server_control_port = UDP_DEFAULT_CONTROL_PORT;
-            c->udp_my_timing_port      = UDP_DEFAULT_TIMING_PORT;
-            c->udp_server_timing_port  = UDP_DEFAULT_TIMING_PORT;
-
-            pa_log_error("aborting RTSP setup, failed creating required sockets");
-
-            break;
-        }
-
-        case STATE_RECORD: {
-            int32_t latency = 0;
-            uint32_t rand;
-            char *alt;
-
-            pa_log_debug("RAOP: RECORD");
-
-            alt = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Latency"));
-            /* Generate a random synchronization source identifier from this session. */
-            pa_random(&rand, sizeof(rand));
-            c->udp_ssrc = rand;
-
-            if (alt)
-                pa_atoi(alt, &latency);
-
-            c->udp_first_packet = true;
-            c->udp_sync_count = 0;
-
-            c->is_recording = true;
-
-            c->udp_record_callback(c->udp_setup_userdata);
-
-            pa_xfree(alt);
-            break;
-        }
-
-        case STATE_SET_PARAMETER: {
-            pa_log_debug("RAOP: SET_PARAMETER");
-
-            break;
-        }
-
-        case STATE_FLUSH: {
-            pa_log_debug("RAOP: FLUSHED");
-
-            c->is_recording = false;
-
-            break;
-        }
-
-        case STATE_TEARDOWN: {
-            pa_log_debug("RAOP: TEARDOWN");
-            pa_assert(c->udp_disconnected_callback);
-            pa_assert(c->rtsp);
-
-            c->is_recording = false;
-
-            pa_rtsp_disconnect(c->rtsp);
-
-            if (c->udp_stream_fd > 0) {
-                pa_close(c->udp_stream_fd);
-                c->udp_stream_fd = -1;
-            }
-
-            pa_log_debug("RTSP control channel closed (teardown)");
-
-            pa_raop_pb_clear(c->packet_buffer);
-
-            pa_rtsp_client_free(c->rtsp);
-            pa_xfree(c->sid);
-            c->rtsp = NULL;
-            c->sid = NULL;
-
-            /*
-              Callback for cleanup -- e.g. pollfd
-
-              Share the disconnected callback since TEARDOWN event
-              is essentially equivalent to DISCONNECTED.
-              In case some special treatment turns out to be required
-              for TEARDOWN in future, a new callback function may be
-              defined and used.
-            */
-            c->udp_disconnected_callback(c->udp_disconnected_userdata);
-
-            /* Control and timing fds are closed by udp_sink_process_msg,
-               after it disables poll */
-            c->udp_control_fd = -1;
-            c->udp_timing_fd = -1;
-
-            break;
-        }
-
-        case STATE_DISCONNECTED: {
-            pa_log_debug("RAOP: DISCONNECTED");
-            pa_assert(c->udp_disconnected_callback);
-            pa_assert(c->rtsp);
-
-            if (c->udp_stream_fd > 0) {
-                pa_close(c->udp_stream_fd);
-                c->udp_stream_fd = -1;
-            }
-
-            pa_log_debug("RTSP control channel closed (disconnected)");
-
-            pa_raop_pb_clear(c->packet_buffer);
-
-            pa_rtsp_client_free(c->rtsp);
-            pa_xfree(c->sid);
-            c->rtsp = NULL;
-            c->sid = NULL;
-
-            c->udp_disconnected_callback(c->udp_disconnected_userdata);
-            /* Control and timing fds are closed by udp_sink_process_msg,
-               after it disables poll */
-            c->udp_control_fd = -1;
-            c->udp_timing_fd = -1;
-
-            break;
-        }
-    }
-}
-
-static void rtsp_authentication_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_rtsp_status status, pa_headerlist *headers, void *userdata) {
-    pa_raop_client *c = userdata;
-
-    pa_assert(c);
-    pa_assert(rtsp);
-    pa_assert(rtsp == c->rtsp);
-
-    switch (state) {
-        case STATE_CONNECT: {
-            char *sci = NULL, *sac = NULL;
-            uint16_t rac;
-            struct {
-                uint32_t ci1;
-                uint32_t ci2;
-            } rci;
-
-            pa_random(&rci, sizeof(rci));
-            /* Generate a random Client-Instance number */
-            sci = pa_sprintf_malloc("%08x%08x",rci.ci1, rci.ci2);
-            pa_rtsp_add_header(c->rtsp, "Client-Instance", sci);
-
-            pa_random(&rac, sizeof(rac));
-            /* Generate a random Apple-Challenge key */
-            pa_raop_base64_encode(&rac, 8*sizeof(rac), &sac);
-            pa_log_debug ("APPLECHALLENGE >> %s | %d", sac, (int) sizeof(rac));
-            rtrimchar(sac, '=');
-            pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
-
-            pa_rtsp_options(c->rtsp);
-
-            pa_xfree(sac);
-            pa_xfree(sci);
-            break;
-        }
-
-        case STATE_OPTIONS: {
-            static bool waiting = false;
-            const char *current = NULL;
-            char space[] = " ";
-            char *token,*ath = NULL;
-            char *publ, *wath, *mth, *val;
-            char *realm = NULL, *nonce = NULL, *response = NULL;
-            char comma[] = ",";
-
-            pa_log_debug("RAOP: OPTIONS");
-            /* We do not consider the Apple-Response */
-            pa_rtsp_remove_header(c->rtsp, "Apple-Challenge");
-
-            if (STATUS_UNAUTHORIZED == status) {
-                wath = pa_xstrdup(pa_headerlist_gets(headers, "WWW-Authenticate"));
-                if (true == waiting) {
-                    pa_xfree(wath);
-                    goto failure;
-                }
-
-                if (wath)
-                    mth = pa_split(wath, space, &current);
-                while ((token = pa_split(wath, comma, &current))) {
-                    val = NULL;
-                    if ((val = strstr(token, "="))) {
-                        if (NULL == realm && val > strstr(token, "realm"))
-                            realm = pa_xstrdup(val + 2);
-                        else if (NULL == nonce && val > strstr(token, "nonce"))
-                            nonce = pa_xstrdup(val + 2);
-                        val = NULL;
-                    }
-
-                    pa_xfree(token);
-                }
-
-                if (pa_safe_streq(mth, "Basic")) {
-                    rtrimchar(realm, '\"');
-
-                    pa_raop_basic_response(USER_NAME, c->pwd, &response);
-                    ath = pa_sprintf_malloc("Basic %s",
-                        response);
-
-                    pa_xfree(response);
-                    pa_xfree(realm);
-                } else if (pa_safe_streq(mth, "Digest")) {
-                    rtrimchar(realm, '\"');
-                    rtrimchar(nonce, '\"');
-
-                    pa_raop_digest_response(USER_NAME, realm, c->pwd, nonce, "*", &response);
-                    ath = pa_sprintf_malloc("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"*\", response=\"%s\"",
-                        USER_NAME, realm, nonce,
-                        response);
-
-                    pa_xfree(response);
-                    pa_xfree(realm);
-                    pa_xfree(nonce);
-                } else {
-                    pa_log_error("unsupported authentication method: %s", mth);
-                    pa_xfree(wath);
-                    pa_xfree(mth);
-                    goto error;
-                }
-
-                pa_xfree(wath);
-                pa_xfree(mth);
-
-                pa_rtsp_add_header(c->rtsp, "Authorization", ath);
-                pa_xfree(ath);
-
-                waiting = true;
-                pa_rtsp_options(c->rtsp);
-                break;
-            }
-
-            if (STATUS_OK == status) {
-                publ = pa_xstrdup(pa_headerlist_gets(headers, "Public"));
-                c->sci = pa_xstrdup(pa_rtsp_get_header(c->rtsp, "Client-Instance"));
-
-                if (c->pwd)
-                    pa_xfree(c->pwd);
-                pa_xfree(publ);
-                c->pwd = NULL;
-            }
-
-            if (c->udp_auth_callback)
-                c->udp_auth_callback((int) status, c->udp_auth_userdata);
-            pa_rtsp_client_free(c->rtsp);
-            c->rtsp = NULL;
-
-            waiting = false;
-            break;
-
-        failure:
-            if (c->udp_auth_callback)
-                c->udp_auth_callback((int) STATUS_UNAUTHORIZED, c->udp_auth_userdata);
-            pa_rtsp_client_free(c->rtsp);
-            c->rtsp = NULL;
-
-            pa_log_error("aborting RTSP authentication, wrong password");
-
-            waiting = false;
-            break;
-
-        error:
-            if (c->udp_auth_callback)
-                c->udp_auth_callback((int) status, c->udp_auth_userdata);
-            pa_rtsp_client_free(c->rtsp);
-            c->rtsp = NULL;
-
-            pa_log_error("aborting RTSP authentication, unexpected failure");
-
-            waiting = false;
-            break;
-        }
-
-        case STATE_ANNOUNCE:
-        case STATE_SETUP:
-        case STATE_RECORD:
-        case STATE_SET_PARAMETER:
-        case STATE_FLUSH:
-        case STATE_TEARDOWN:
-        case STATE_DISCONNECTED:
-        default: {
-            if (c->udp_auth_callback)
-                c->udp_auth_callback((int) STATUS_BAD_REQUEST, c->udp_auth_userdata);
-            pa_rtsp_client_free(c->rtsp);
-            c->rtsp = NULL;
-
-            if (c->sci)
-                pa_xfree(c->sci);
-            c->sci = NULL;
-
-            break;
-        }
-    }
-}
-
-pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_protocol_t protocol) {
-    pa_raop_client* c;
-    pa_parsed_address a;
-    pa_sample_spec ss;
-
-    pa_assert(core);
-    pa_assert(host);
-
-    if (pa_parse_address(host, &a) < 0)
-        return NULL;
-
-    if (a.type == PA_PARSED_ADDRESS_UNIX) {
-        pa_xfree(a.path_or_host);
-        return NULL;
-    }
-
-    c = pa_xnew0(pa_raop_client, 1);
-    c->core = core;
-    c->tcp_fd = -1;
-    c->protocol = protocol;
-    c->udp_stream_fd = -1;
-    c->udp_control_fd = -1;
-    c->udp_timing_fd = -1;
-
-    c->encryption = 0;
-    c->aes = NULL;
-
-    c->udp_my_control_port     = UDP_DEFAULT_CONTROL_PORT;
-    c->udp_server_control_port = UDP_DEFAULT_CONTROL_PORT;
-    c->udp_my_timing_port      = UDP_DEFAULT_TIMING_PORT;
-    c->udp_server_timing_port  = UDP_DEFAULT_TIMING_PORT;
-
-    c->host = a.path_or_host;
-    if (a.port)
-        c->port = a.port;
-    else
-        c->port = DEFAULT_RAOP_PORT;
-
-    c->is_recording = false;
-
-    c->udp_first_packet = true;
-
-    ss = core->default_sample_spec;
-    /* Packet sync interval should be around 1s. */
-    c->udp_sync_interval = ss.rate / UDP_FRAMES_PER_PACKET;
-    c->udp_sync_count = 0;
-
-    if (c->protocol == RAOP_TCP) {
-        if (pa_raop_client_connect(c)) {
-            pa_raop_client_free(c);
-            return NULL;
-        }
-    } else
-        c->packet_buffer = pa_raop_pb_new(UDP_DEFAULT_PKT_BUF_SIZE);
-
-    return c;
-}
-
-void pa_raop_client_free(pa_raop_client *c) {
-    pa_assert(c);
-
-    pa_raop_pb_delete(c->packet_buffer);
-
-    if (c->sid)
-        pa_xfree(c->sid);
-    if (c->sci)
-        pa_xfree(c->sci);
-    c->sci = c->sid = NULL;
-
-    if (c->aes)
-        pa_raop_secret_free(c->aes);
-    if (c->rtsp)
-        pa_rtsp_client_free(c->rtsp);
-    c->rtsp = NULL;
-
-    pa_xfree(c->host);
-    pa_xfree(c);
-}
-
-int pa_raop_client_authenticate (pa_raop_client *c, const char *password) {
-
-    pa_assert(c);
-
-    if (c->rtsp || c->pwd) {
-        pa_log_debug("Connection already in progress");
-        return 0;
-    }
-
-    c->pwd = NULL;
-    if (password)
-        c->pwd = pa_xstrdup(password);
-    c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, USER_AGENT);
-
-    pa_assert(c->rtsp);
-
-    pa_rtsp_set_callback(c->rtsp, rtsp_authentication_cb, c);
-    return pa_rtsp_connect(c->rtsp);
-}
-
-int pa_raop_client_connect(pa_raop_client *c) {
-    char *sci;
-    struct {
-        uint32_t a;
-        uint32_t b;
-        uint32_t c;
-    } rand_data;
-
-    pa_assert(c);
-
-    if (c->rtsp) {
-        pa_log_debug("Connection already in progress");
-        return 0;
-    }
-
-    if (c->protocol == RAOP_TCP)
-        c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, "iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
-    else
-        c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, USER_AGENT);
-
-    /* Generate random instance id. */
-    pa_random(&rand_data, sizeof(rand_data));
-    c->sid = pa_sprintf_malloc("%u", rand_data.a);
-    sci = pa_sprintf_malloc("%08x%08x",rand_data.b, rand_data.c);
-    pa_rtsp_add_header(c->rtsp, "Client-Instance", sci);
-    pa_xfree(sci);
-    if (c->protocol == RAOP_TCP)
-        pa_rtsp_set_callback(c->rtsp, tcp_rtsp_cb, c);
-    else
-        pa_rtsp_set_callback(c->rtsp, udp_rtsp_cb, c);
-
-    c->is_recording = false;
-
-    return pa_rtsp_connect(c->rtsp);
-}
-
-int pa_raop_client_flush(pa_raop_client *c) {
-    int rv = 0;
-
-    pa_assert(c);
-
-    if (c->rtsp != NULL) {
-        rv = pa_rtsp_flush(c->rtsp, c->seq, c->rtptime);
-        c->udp_sync_count = 0;
-    }
-
-    return rv;
-}
-
-int pa_raop_client_teardown(pa_raop_client *c) {
-    int rv = 0;
-
-    pa_assert(c);
-
-    if (c->rtsp != NULL)
-        rv = pa_rtsp_teardown(c->rtsp);
-
-    return rv;
-}
-
-int pa_raop_client_udp_is_authenticated(pa_raop_client *c) {
-    int rv = 0;
-
-    pa_assert(c);
-
-    if (c->sci != NULL)
-        rv = 1;
-
-    return rv;
-}
-
-int pa_raop_client_udp_is_alive(pa_raop_client *c) {
-    int rv = 0;
-
-    pa_assert(c);
-
-    if (c->udp_stream_fd > 0)
-        rv = 1;
-
-    return rv;
-}
-
-int pa_raop_client_udp_can_stream(pa_raop_client *c) {
-    int rv = 0;
-
-    pa_assert(c);
-
-    if (c->is_recording && c->udp_stream_fd > 0)
-        rv = 1;
-
-    return rv;
-}
-
-int pa_raop_client_udp_stream(pa_raop_client *c) {
-    int rv = 0;
-
-    pa_assert(c);
-
-    if (c->rtsp != NULL && c->udp_stream_fd > 0) {
-        if (!c->is_recording) {
-            c->udp_first_packet = true;
-            c->udp_sync_count = 0;
-            c->is_recording = true;
-         }
-
-        rv = 1;
-    }
-
-    return rv;
-}
-
-int pa_raop_client_udp_handle_timing_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size) {
-    const uint32_t * data = NULL;
-    uint8_t payload = 0;
-    struct timeval tv;
-    uint64_t rci = 0;
-    int rv = 0;
-
-    pa_assert(c);
-    pa_assert(packet);
-
-    /* Timing packets are 32 bytes long: 1 x 8 RTP header (no ssrc) + 3 x 8 NTP timestamps. */
-    if (size != 32 || packet[0] != 0x80)
-    {
-        pa_log_debug("Received an invalid timing packet.");
-        return 1;
-    }
-
-    data = (uint32_t *) (packet + sizeof(udp_timming_header));
-    rci = timeval_to_ntp(pa_rtclock_get(&tv));
-    /* The market bit is always set (see rfc3550 for packet structure) ! */
-    payload = packet[1] ^ 0x80;
-    switch (payload) {
-        case UDP_PAYLOAD_TIMING_REQUEST:
-            rv = udp_send_timing_packet(c, data, rci);
-            break;
-        case UDP_PAYLOAD_TIMING_RESPONSE:
-        default:
-            pa_log_debug("Got an unexpected payload type on timing channel !");
-            return 1;
-    }
-
-    return rv;
-}
-
-static int udp_resend_packets(pa_raop_client *c, uint16_t seq_num, uint16_t num_packets) {
-    int rv = -1;
-    uint8_t *data = NULL;
-    ssize_t len = 0;
-    int i = 0;
-
-    pa_assert(c);
-    pa_assert(num_packets > 0);
-    pa_assert(c->packet_buffer);
-
-    for (i = seq_num; i < seq_num + num_packets; i++) {
-        len = pa_raop_pb_read_packet(c->packet_buffer, i, (uint8_t **) &data);
-
-        if (len > 0) {
-            ssize_t r;
-
-            /* Obtained buffer has a header room for retransmission
-               header */
-            udp_build_retrans_header((uint32_t *) data, len, seq_num);
-            r = udp_send_audio_packet(c, true /* retrans */, data, len);
-            if (r == len)
-                rv = 0;
-            else
-                rv = -1;
-        } else
-            pa_log_debug("Packet not found in retrans buffer: %u", i);
-    }
-
-    return rv;
-}
-
-int pa_raop_client_udp_handle_control_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size) {
-    uint8_t payload = 0;
-    int rv = 0;
-
-    uint16_t seq_num;
-    uint16_t num_packets;
-
-    pa_assert(c);
-    pa_assert(packet);
-
-    if ((size != 20 && size != 8) || packet[0] != 0x80)
-    {
-        pa_log_debug("Received an invalid control packet.");
-        return 1;
-    }
-
-    /* The market bit is always set (see rfc3550 for packet structure) ! */
-
-    payload = packet[1] ^ 0x80;
-    switch (payload) {
-        case UDP_PAYLOAD_RETRANSMIT_REQUEST:
-            pa_assert(size == 8);
-
-            /* Requested start sequence number */
-            seq_num = ((uint16_t) packet[4]) << 8;
-            seq_num |= (uint16_t) packet[5];
-            /* Number of requested packets starting at requested seq. number */
-            num_packets = (uint16_t) packet[6] << 8;
-            num_packets |= (uint16_t) packet[7];
-            pa_log_debug("Resending %d packets starting at %d", num_packets, seq_num);
-            rv = udp_resend_packets(c, seq_num, num_packets);
-            break;
-
-        case UDP_PAYLOAD_RETRANSMIT_REPLY:
-            pa_log_debug("Received a retransmit reply packet on control port (this should never happen)");
-            break;
-
-        default:
-            pa_log_debug("Got an unexpected payload type on control channel: %u !", payload);
-            return 1;
-    }
-
-    return rv;
-}
-
-int pa_raop_client_udp_get_blocks_size(pa_raop_client *c, size_t *size) {
-    int rv = 0;
-
-    pa_assert(c);
-    pa_assert(size);
-
-    *size = UDP_FRAMES_PER_PACKET;
-
-    return rv;
-}
-
-ssize_t pa_raop_client_udp_send_audio_packet(pa_raop_client *c, pa_memchunk *block) {
-    uint8_t *buf = NULL;
-    ssize_t len;
-
-    pa_assert(c);
-    pa_assert(block);
-
-    /* Sync RTP & NTP timestamp if required. */
-    if (c->udp_first_packet || c->udp_sync_count >= c->udp_sync_interval) {
-        udp_send_sync_packet(c, c->rtptime);
-        c->udp_sync_count = 0;
-    } else {
-        c->udp_sync_count++;
-    }
-
-    buf = pa_memblock_acquire(block->memblock);
-    pa_assert(buf);
-    pa_assert(block->length > 0);
-    udp_build_audio_header(c, (uint32_t *) (buf + block->index), block->length);
-    len = udp_send_audio_packet(c, false, buf + block->index, block->length);
-
-    /* Store packet for resending in the packet buffer */
-    pa_raop_pb_write_packet(c->packet_buffer, c->seq, buf + block->index,
-                            block->length);
-
-    c->seq++;
-
-    pa_memblock_release(block->memblock);
-
-    if (len > 0) {
-        pa_assert((size_t) len <= block->length);
-        /* UDP packet has to be sent at once, so it is meaningless to
-           preseve the partial data
-           FIXME: This won't happen at least in *NIX systems?? */
-        if (block->length > (size_t) len) {
-            pa_log_warn("Tried to send %zu bytes but managed to send %zu bytes", block->length, len);
-            len = block->length;
-        }
-        block->index += block->length;
-        block->length = 0;
-    }
-
-    if (c->udp_first_packet)
-        c->udp_first_packet = false;
-
-    return len;
-}
-
-void pa_raop_client_set_encryption(pa_raop_client *c, int encryption) {
-    pa_assert(c);
-
-    c->encryption = encryption;
-    if (c->encryption)
-        c->aes = pa_raop_secret_new();
-}
-
-/* Adjust volume so that it fits into VOLUME_DEF <= v <= 0 dB */
-pa_volume_t pa_raop_client_adjust_volume(pa_raop_client *c, pa_volume_t volume) {
-    double minv, maxv;
-
-    if (c->protocol != RAOP_UDP)
-        return volume;
-
-    maxv = pa_sw_volume_from_dB(0.0);
-    minv = maxv * pow(10.0, (double) VOLUME_DEF / 60.0);
-
-    return volume - volume * (minv / maxv) + minv;
-}
-
-int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume) {
-    int rv = 0;
-    double db;
-    char *param;
-
-    pa_assert(c);
-
-    db = pa_sw_volume_to_dB(volume);
-    if (db < VOLUME_MIN)
-        db = VOLUME_MIN;
-    else if (db > VOLUME_MAX)
-        db = VOLUME_MAX;
-
-    pa_log_debug("volume=%u db=%.6f", volume, db);
-
-    param = pa_sprintf_malloc("volume: %0.6f\r\n",  db);
-
-    /* We just hit and hope, cannot wait for the callback. */
-    if (c->rtsp != NULL && pa_rtsp_exec_ready(c->rtsp))
-        rv = pa_rtsp_setparameter(c->rtsp, param);
-    pa_xfree(param);
-
-    return rv;
-}
-
-int pa_raop_client_encode_sample(pa_raop_client *c, pa_memchunk *raw, pa_memchunk *encoded) {
-    uint16_t len;
-    size_t bufmax;
-    uint8_t *bp, bpos;
-    uint8_t *ibp, *maxibp;
-    int size;
-    uint8_t *b, *p;
-    uint32_t bsize;
-    size_t length;
-    const uint8_t *header;
-    int header_size;
-
-    pa_assert(c);
-    pa_assert(raw);
-    pa_assert(raw->memblock);
-    pa_assert(raw->length > 0);
-    pa_assert(encoded);
-
-    if (c->protocol == RAOP_TCP) {
-        header = tcp_audio_header;
-        header_size = sizeof(tcp_audio_header);
-    } else {
-        header = udp_audio_header;
-        header_size = sizeof(udp_audio_header);
-    }
-
-    /* We have to send 4 byte chunks */
-    bsize = (int)(raw->length / 4);
-    length = bsize * 4;
-
-    /* Leave 16 bytes extra to allow for the ALAC header which is about 55 bits. */
-    bufmax = length + header_size + 16;
-    pa_memchunk_reset(encoded);
-    encoded->memblock = pa_memblock_new(c->core->mempool, bufmax);
-    b = pa_memblock_acquire(encoded->memblock);
-    memcpy(b, header, header_size);
-
-    /* Now write the actual samples. */
-    bp = b + header_size;
-    size = bpos = 0;
-    bit_writer(&bp,&bpos,&size,1,3); /* channel=1, stereo */
-    bit_writer(&bp,&bpos,&size,0,4); /* Unknown */
-    bit_writer(&bp,&bpos,&size,0,8); /* Unknown */
-    bit_writer(&bp,&bpos,&size,0,4); /* Unknown */
-    bit_writer(&bp,&bpos,&size,1,1); /* Hassize */
-    bit_writer(&bp,&bpos,&size,0,2); /* Unused */
-    bit_writer(&bp,&bpos,&size,1,1); /* Is-not-compressed */
-
-    /* Size of data, integer, big endian. */
-    bit_writer(&bp,&bpos,&size,(bsize>>24)&0xff,8);
-    bit_writer(&bp,&bpos,&size,(bsize>>16)&0xff,8);
-    bit_writer(&bp,&bpos,&size,(bsize>>8)&0xff,8);
-    bit_writer(&bp,&bpos,&size,(bsize)&0xff,8);
-
-    p = pa_memblock_acquire(raw->memblock);
-    p += raw->index;
-    ibp = p;
-    maxibp = p + raw->length - 4;
-    while (ibp <= maxibp) {
-        /* Byte swap stereo data. */
-        bit_writer(&bp,&bpos,&size,*(ibp+1),8);
-        bit_writer(&bp,&bpos,&size,*(ibp+0),8);
-        bit_writer(&bp,&bpos,&size,*(ibp+3),8);
-        bit_writer(&bp,&bpos,&size,*(ibp+2),8);
-        ibp += 4;
-        raw->index += 4;
-        raw->length -= 4;
-    }
-    if (c->protocol == RAOP_UDP)
-        c->rtptime += (ibp - p) / 4;
-    pa_memblock_release(raw->memblock);
-    encoded->length = header_size + size;
-
-    if (c->protocol == RAOP_TCP) {
-        /* Store the length (endian swapped: make this better). */
-        len = size + header_size - 4;
-        *(b + 2) = len >> 8;
-        *(b + 3) = len & 0xff;
-    }
-
-    if (c->encryption) {
-        /* Encrypt our data. */
-        pa_raop_aes_encrypt(c->aes, (b + header_size), size);
-    }
-
-    /* We're done with the chunk. */
-    pa_memblock_release(encoded->memblock);
-
-    return 0;
-}
-
-void pa_raop_client_tcp_set_callback(pa_raop_client *c, pa_raop_client_cb_t callback, void *userdata) {
-    pa_assert(c);
-
-    c->tcp_callback = callback;
-    c->tcp_userdata = userdata;
-}
-
-void pa_raop_client_tcp_set_closed_callback(pa_raop_client *c, pa_raop_client_closed_cb_t callback, void *userdata) {
-    pa_assert(c);
-
-    c->tcp_closed_callback = callback;
-    c->tcp_closed_userdata = userdata;
-}
-
-void pa_raop_client_udp_set_auth_callback(pa_raop_client *c, pa_raop_client_auth_cb_t callback, void *userdata) {
-    pa_assert(c);
-
-    c->udp_auth_callback = callback;
-    c->udp_auth_userdata = userdata;
-}
-
-void pa_raop_client_udp_set_setup_callback(pa_raop_client *c, pa_raop_client_setup_cb_t callback, void *userdata) {
-    pa_assert(c);
-
-    c->udp_setup_callback = callback;
-    c->udp_setup_userdata = userdata;
-}
-
-void pa_raop_client_udp_set_record_callback(pa_raop_client *c, pa_raop_client_record_cb_t callback, void *userdata) {
-    pa_assert(c);
-
-    c->udp_record_callback = callback;
-    c->udp_record_userdata = userdata;
-}
-
-void pa_raop_client_udp_set_disconnected_callback(pa_raop_client *c, pa_raop_client_disconnected_cb_t callback, void *userdata) {
-    pa_assert(c);
-
-    c->udp_disconnected_callback = callback;
-    c->udp_disconnected_userdata = userdata;
-}
diff --git a/src/modules/raop/raop_client.h b/src/modules/raop/raop_client.h
deleted file mode 100644
index e208349..0000000
--- a/src/modules/raop/raop_client.h
+++ /dev/null
@@ -1,78 +0,0 @@
-#ifndef fooraopclientfoo
-#define fooraopclientfoo
-
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2008 Colin Guthrie
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  PulseAudio 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 Lesser General Public License
-  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <pulse/volume.h>
-
-#include <pulsecore/core.h>
-#include <pulsecore/memchunk.h>
-
-#define UDP_FRAMES_PER_PACKET 352
-
-typedef enum pa_raop_protocol {
-    RAOP_TCP,
-    RAOP_UDP,
-} pa_raop_protocol_t;
-
-typedef struct pa_raop_client pa_raop_client;
-
-pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_protocol_t protocol);
-void pa_raop_client_free(pa_raop_client *c);
-
-int pa_raop_client_authenticate (pa_raop_client *c, const char *password);
-int pa_raop_client_connect(pa_raop_client *c);
-int pa_raop_client_flush(pa_raop_client *c);
-int pa_raop_client_teardown(pa_raop_client *c);
-
-int pa_raop_client_udp_is_authenticated(pa_raop_client *c);
-int pa_raop_client_udp_is_alive(pa_raop_client *c);
-int pa_raop_client_udp_can_stream(pa_raop_client *c);
-int pa_raop_client_udp_stream(pa_raop_client *c);
-
-void pa_raop_client_set_encryption(pa_raop_client *c, int encryption);
-pa_volume_t pa_raop_client_adjust_volume(pa_raop_client *c, pa_volume_t volume);
-int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume);
-int pa_raop_client_encode_sample(pa_raop_client *c, pa_memchunk *raw, pa_memchunk *encoded);
-
-int pa_raop_client_udp_handle_timing_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size);
-int pa_raop_client_udp_handle_control_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size);
-int pa_raop_client_udp_get_blocks_size(pa_raop_client *c, size_t *size);
-ssize_t pa_raop_client_udp_send_audio_packet(pa_raop_client *c, pa_memchunk *block);
-
-typedef void (*pa_raop_client_cb_t)(int fd, void *userdata);
-void pa_raop_client_tcp_set_callback(pa_raop_client *c, pa_raop_client_cb_t callback, void *userdata);
-
-typedef void (*pa_raop_client_closed_cb_t)(void *userdata);
-void pa_raop_client_tcp_set_closed_callback(pa_raop_client *c, pa_raop_client_closed_cb_t callback, void *userdata);
-
-typedef void (*pa_raop_client_auth_cb_t)(int status, void *userdata);
-void pa_raop_client_udp_set_auth_callback(pa_raop_client *c, pa_raop_client_auth_cb_t callback, void *userdata);
-
-typedef void (*pa_raop_client_setup_cb_t)(int control_fd, int timing_fd, void *userdata);
-void pa_raop_client_udp_set_setup_callback(pa_raop_client *c, pa_raop_client_setup_cb_t callback, void *userdata);
-
-typedef void (*pa_raop_client_record_cb_t)(void *userdata);
-void pa_raop_client_udp_set_record_callback(pa_raop_client *c, pa_raop_client_record_cb_t callback, void *userdata);
-
-typedef void (*pa_raop_client_disconnected_cb_t)(void *userdata);
-void pa_raop_client_udp_set_disconnected_callback(pa_raop_client *c, pa_raop_client_disconnected_cb_t callback, void *userdata);
-
-#endif
diff --git a/src/modules/raop/raop_crypto.c b/src/modules/raop/raop_crypto.c
deleted file mode 100644
index 4d08e26..0000000
--- a/src/modules/raop/raop_crypto.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2013 Martin Blanchard
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  PulseAudio 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 Lesser General Public License
-  along with PulseAudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-
-#include <openssl/err.h>
-#include <openssl/aes.h>
-#include <openssl/rsa.h>
-
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/macro.h>
-#include <pulsecore/random.h>
-
-#include "raop_crypto.h"
-#include "raop_util.h"
-
-#define AES_CHUNK_SIZE 16
-
-/* Openssl 1.1.0 broke compatibility. Before 1.1.0 we had to set RSA->n and
- * RSA->e manually, but after 1.1.0 the RSA struct is opaque and we have to use
- * RSA_set0_key(). RSA_set0_key() is a new function added in 1.1.0. We could
- * depend on openssl 1.1.0, but it may take some time before distributions will
- * be able to upgrade to the new openssl version. To insulate ourselves from
- * such transition problems, let's implement RSA_set0_key() ourselves if it's
- * not available. */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-static int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) {
-    r->n = n;
-    r->e = e;
-    return 1;
-}
-#endif
-
-struct pa_raop_secret {
-    uint8_t key[AES_CHUNK_SIZE]; /* Key for aes-cbc */
-    uint8_t iv[AES_CHUNK_SIZE];  /* Initialization vector for cbc */
-    AES_KEY aes;                 /* AES encryption */
-};
-
-static const char rsa_modulus[] =
-    "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC"
-    "5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDR"
-    "KSKv6kDqnw4UwPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuB"
-    "OitnZ/bDzPHrTOZz0Dew0uowxf/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJ"
-    "Q+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/UAaHqn9JdsBWLUEpVviYnh"
-    "imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew==";
-
-static const char rsa_exponent[] =
-    "AQAB";
-
-static int rsa_encrypt(uint8_t *data, int len, uint8_t *str) {
-    uint8_t modules[256];
-    uint8_t exponent[8];
-    int size;
-    RSA *rsa;
-    BIGNUM *n_bn;
-    BIGNUM *e_bn;
-
-    pa_assert(data);
-    pa_assert(str);
-
-    rsa = RSA_new();
-    size = pa_raop_base64_decode(rsa_modulus, modules);
-    n_bn = BN_bin2bn(modules, size, NULL);
-    size = pa_raop_base64_decode(rsa_exponent, exponent);
-    e_bn = BN_bin2bn(exponent, size, NULL);
-    RSA_set0_key(rsa, n_bn, e_bn, NULL);
-
-    size = RSA_public_encrypt(len, data, str, rsa, RSA_PKCS1_OAEP_PADDING);
-
-    RSA_free(rsa);
-    return size;
-}
-
-pa_raop_secret* pa_raop_secret_new(void) {
-    pa_raop_secret *s = pa_xnew0(pa_raop_secret, 1);
-
-    pa_assert(s);
-
-    pa_random(s->key, sizeof(s->key));
-    AES_set_encrypt_key(s->key, 128, &s->aes);
-    pa_random(s->iv, sizeof(s->iv));
-
-    return s;
-}
-
-void pa_raop_secret_free(pa_raop_secret *s) {
-    pa_assert(s);
-
-    pa_xfree(s);
-}
-
-char* pa_raop_secret_get_iv(pa_raop_secret *s) {
-    char *base64_iv = NULL;
-
-    pa_assert(s);
-
-    pa_raop_base64_encode(s->iv, AES_CHUNK_SIZE, &base64_iv);
-
-    return base64_iv;
-}
-
-char* pa_raop_secret_get_key(pa_raop_secret *s) {
-    char *base64_key = NULL;
-    uint8_t rsa_key[512];
-    int size = 0;
-
-    pa_assert(s);
-
-    /* Encrypt our AES public key to send to the device */
-    size = rsa_encrypt(s->key, AES_CHUNK_SIZE, rsa_key);
-    pa_raop_base64_encode(rsa_key, size, &base64_key);
-
-    return base64_key;
-}
-
-int pa_raop_aes_encrypt(pa_raop_secret *s, uint8_t *data, int len) {
-    static uint8_t nv[AES_CHUNK_SIZE];
-    uint8_t *buffer;
-    int i = 0, j;
-
-    pa_assert(s);
-    pa_assert(data);
-
-    memcpy(nv, s->iv, AES_CHUNK_SIZE);
-
-    while (i + AES_CHUNK_SIZE <= len) {
-        buffer = data + i;
-        for (j = 0; j < AES_CHUNK_SIZE; ++j)
-            buffer[j] ^= nv[j];
-
-        AES_encrypt(buffer, buffer, &s->aes);
-
-        memcpy(nv, buffer, AES_CHUNK_SIZE);
-        i += AES_CHUNK_SIZE;
-    }
-
-    return i;
-}
diff --git a/src/modules/raop/raop_crypto.h b/src/modules/raop/raop_crypto.h
deleted file mode 100644
index 65f7577..0000000
--- a/src/modules/raop/raop_crypto.h
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef fooraopcryptofoo
-#define fooraopcryptofoo
-
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2013 Martin Blanchard
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  PulseAudio 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 Lesser General Public License
-  along with PulseAudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-typedef struct pa_raop_secret pa_raop_secret;
-
-pa_raop_secret* pa_raop_secret_new(void);
-void pa_raop_secret_free(pa_raop_secret *s);
-
-char* pa_raop_secret_get_iv(pa_raop_secret *s);
-char* pa_raop_secret_get_key(pa_raop_secret *s);
-
-int pa_raop_aes_encrypt(pa_raop_secret *s, uint8_t *data, int len);
-
-#endif
diff --git a/src/modules/raop/raop_packet_buffer.c b/src/modules/raop/raop_packet_buffer.c
deleted file mode 100644
index b8c1bc8..0000000
--- a/src/modules/raop/raop_packet_buffer.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/***
-  Circular buffer for RTP audio packets with random access support
-  by RTP sequence number.
-
-  Copyright 2013 Matthias Wabersich, Hajime Fujita
-
-  This is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  This 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 Lesser General Public License
-  along with PulseAudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-
-***/
-
-#include <stdlib.h>
-#include <limits.h>
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <pulsecore/core-error.h>
-#include "raop_client.h"
-
-#include "raop_packet_buffer.h"
-
-/* FRAMES_PER_PACKET*2*2 + sizeof(udp_audio_header) + sizeof(ALAC header), unencoded */
-#define PACKET_SIZE_MAX (UDP_FRAMES_PER_PACKET*2*2 + 12 + 7)
-/* Header room for packet retransmission header */
-#define RETRANS_HEADER_ROOM 4
-
-/* Packet element */
-struct pa_raop_packet_element {
-    uint16_t  seq_num; /* RTP sequence number (in host byte order) */
-    ssize_t   length;  /* Actual packet length */
-    /* Packet data including RTP header */
-    uint8_t   data[PACKET_SIZE_MAX + RETRANS_HEADER_ROOM];
-};
-
-/* Buffer struct */
-struct pa_raop_packet_buffer {
-    size_t   size;          /* max number of packets in buffer */
-    size_t   start;         /* index of oldest packet */
-    size_t   count;         /* number of packets in buffer */
-    uint16_t first_seq_num; /* Sequence number of first packet in buffer */
-    uint16_t latest_seq_num; /* Debug purpose */
-    pa_raop_packet_element *packets; /* Packet element pointer */
-};
-
-pa_raop_packet_buffer *pa_raop_pb_new(size_t size) {
-    pa_raop_packet_buffer *pb = pa_xmalloc0(sizeof(*pb));
-
-    pb->size = size;
-    pb->packets = (pa_raop_packet_element *)
-        pa_xmalloc(size * sizeof(pa_raop_packet_element));
-
-    pa_raop_pb_clear(pb);
-
-    return pb;
-}
-
-void pa_raop_pb_clear(pa_raop_packet_buffer *pb) {
-    pb->start = 0;
-    pb->count = 0;
-    pb->first_seq_num = 0;
-    pb->latest_seq_num = 0;
-    memset(pb->packets, 0, pb->size * sizeof(pa_raop_packet_element));
-}
-
-void pa_raop_pb_delete(pa_raop_packet_buffer *pb) {
-    pa_xfree(pb->packets);
-    pa_xfree(pb);
-}
-
-static int pb_is_full(pa_raop_packet_buffer *pb) {
-    return pb->count == pb->size;
-}
-
-static int pb_is_empty(pa_raop_packet_buffer *pb) {
-    return pb->count == 0;
-}
-
-static pa_raop_packet_element *pb_prepare_write(pa_raop_packet_buffer *pb, uint16_t seq) {
-    size_t end = (pb->start + pb->count) % pb->size;
-    pa_raop_packet_element *packet;
-
-    /* Set first packet sequence number in buffer if buffer is empty */
-    if (pb_is_empty(pb))
-        pb->first_seq_num = seq;
-    else
-        pa_assert((uint16_t) (pb->latest_seq_num + 1) == seq);
-
-    packet = &pb->packets[end];
-
-    if (pb_is_full(pb)) {
-        pb->start = (pb->start + 1) % pb->size; /* full, overwrite */
-
-        /* Set first packet sequence number in buffer
-           to new start packet sequence number */
-        pb->first_seq_num = pb->packets[pb->start].seq_num;
-    } else
-        ++ pb->count;
-
-    pb->latest_seq_num = seq;
-
-    return packet;
-}
-
-/* Write packet data to packet buffer */
-void pa_raop_pb_write_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, const uint8_t *packet_data, ssize_t packet_length) {
-    pa_raop_packet_element *packet;
-
-    pa_assert(pb);
-    pa_assert(packet_data);
-    pa_assert(packet_length <= PACKET_SIZE_MAX);
-
-    packet = pb_prepare_write(pb, seq_num);
-    packet->seq_num = seq_num;
-    packet->length = packet_length + RETRANS_HEADER_ROOM;
-
-    /* Insert RETRANS_HEADER_ROOM bytes in front of packet data,
-       for retransmission header */
-    memset(packet->data, 0, RETRANS_HEADER_ROOM);
-    memcpy(packet->data + RETRANS_HEADER_ROOM, packet_data, packet_length);
-}
-
-/* l < r?, considers wrapping */
-static bool seq_lt(uint16_t l, uint16_t r) {
-    return l - r > USHRT_MAX/2;
-}
-
-/* Random access to packet from buffer by sequence number for (re-)sending. */
-ssize_t pa_raop_pb_read_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, uint8_t **packet_data) {
-    uint16_t index = 0; /* Index of requested packet */
-    pa_raop_packet_element *packet;
-
-    /* If the buffer is empty, there is no use in calculating indices */
-    if (pb_is_empty(pb))
-        return -1;
-
-    /* If the requested packet is too old (seq_num below first seq number
-       in buffer) or too young (seq_num greater than current seq number),
-       do nothing and return */
-    if (seq_lt(seq_num, pb->first_seq_num))
-        return -1;
-
-    index = (uint16_t) (seq_num - pb->first_seq_num);
-    if (index >= pb->count)
-        return -1;
-
-    /*  Index of the requested packet in the buffer is calculated
-        using the first sequence number stored in the buffer.
-        The offset (seq_num - first_seq_num) is used to access the array. */
-    packet = &pb->packets[(pb->start + index) % pb->size];
-
-    pa_assert(packet->data[RETRANS_HEADER_ROOM + 2] == (seq_num >> 8));
-    pa_assert(packet->data[RETRANS_HEADER_ROOM + 3] == (seq_num & 0xff));
-    pa_assert(packet_data);
-
-    *packet_data = packet->data;
-
-    return packet->length;
-}
diff --git a/src/modules/raop/raop_packet_buffer.h b/src/modules/raop/raop_packet_buffer.h
deleted file mode 100644
index d8a08a0..0000000
--- a/src/modules/raop/raop_packet_buffer.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef RAOP_PACKET_BUFFER_H_INCLUDED
-#define RAOP_PACKET_BUFFER_H_INCLUDED
-
-/***
-  Circular buffer for RTP audio packets with random access support
-  by RTP sequence number.
-
-  Copyright 2013 Matthias Wabersich, Hajime Fujita
-
-  This is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  This 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 Lesser General Public License
-  along with PulseAudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-
-***/
-
-struct pa_raop_packet_element;
-typedef struct pa_raop_packet_element pa_raop_packet_element;
-
-struct pa_raop_packet_buffer;
-typedef struct pa_raop_packet_buffer pa_raop_packet_buffer;
-
-/* Allocates a new circular packet buffer
-   size: Maximum number of packets to store */
-pa_raop_packet_buffer *pa_raop_pb_new(size_t size);
-void pa_raop_pb_clear(pa_raop_packet_buffer *pb);
-void pa_raop_pb_delete(pa_raop_packet_buffer *pb);
-
-void pa_raop_pb_write_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, const uint8_t *packet_data, ssize_t packet_length);
-ssize_t pa_raop_pb_read_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, uint8_t **packet_data);
-
-#endif /* RAOP_PACKET_BUFFER_H_INCLUDED */
diff --git a/src/modules/raop/raop_util.c b/src/modules/raop/raop_util.c
deleted file mode 100644
index 709ec27..0000000
--- a/src/modules/raop/raop_util.c
+++ /dev/null
@@ -1,210 +0,0 @@
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2013 Martin Blanchard
-  Copyright Kungliga Tekniska Høgskolan & Colin Guthrie
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  PulseAudio 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 Lesser General Public License
-  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
-***/
-
-/***
-  The base64 implementation was originally inspired by a file developed
-  by Kungliga Tekniska högskolan.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <openssl/err.h>
-#include <openssl/md5.h>
-
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/core-util.h>
-#include <pulsecore/macro.h>
-
-#include "raop_util.h"
-
-#ifndef MD5_DIGEST_LENGTH
-#define MD5_DIGEST_LENGTH 16
-#endif
-
-#define MD5_HASH_LENGTH (2*MD5_DIGEST_LENGTH)
-
-#define BASE64_DECODE_ERROR 0xffffffff
-
-static const char base64_chars[] =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-static int char_position(char c) {
-    if (c >= 'A' && c <= 'Z')
-        return c - 'A' + 0;
-    if (c >= 'a' && c <= 'z')
-        return c - 'a' + 26;
-    if (c >= '0' && c <= '9')
-        return c - '0' + 52;
-    if (c == '+')
-        return 62;
-    if (c == '/')
-        return 63;
-
-    return -1;
-}
-
-static unsigned int token_decode(const char *token) {
-    unsigned int val = 0;
-    int marker = 0;
-    int i;
-
-    if (strlen(token) < 4)
-        return BASE64_DECODE_ERROR;
-    for (i = 0; i < 4; i++) {
-        val *= 64;
-        if (token[i] == '=')
-            marker++;
-        else if (marker > 0)
-            return BASE64_DECODE_ERROR;
-        else {
-            int lpos = char_position(token[i]);
-            if (lpos < 0)
-                return BASE64_DECODE_ERROR;
-            val += lpos;
-        }
-    }
-
-    if (marker > 2)
-        return BASE64_DECODE_ERROR;
-
-    return (marker << 24) | val;
-}
-
-int pa_raop_base64_encode(const void *data, int len, char **str) {
-    const unsigned char *q;
-    char *p, *s = NULL;
-    int i, c;
-
-    pa_assert(data);
-    pa_assert(str);
-
-    p = s = pa_xnew(char, len * 4 / 3 + 4);
-    q = (const unsigned char *) data;
-    for (i = 0; i < len;) {
-        c = q[i++];
-        c *= 256;
-        if (i < len)
-            c += q[i];
-        i++;
-        c *= 256;
-        if (i < len)
-            c += q[i];
-        i++;
-        p[0] = base64_chars[(c & 0x00fc0000) >> 18];
-        p[1] = base64_chars[(c & 0x0003f000) >> 12];
-        p[2] = base64_chars[(c & 0x00000fc0) >> 6];
-        p[3] = base64_chars[(c & 0x0000003f) >> 0];
-        if (i > len)
-            p[3] = '=';
-        if (i > len + 1)
-            p[2] = '=';
-        p += 4;
-    }
-
-    *p = 0;
-    *str = s;
-    return strlen(s);
-}
-
-int pa_raop_base64_decode(const char *str, void *data) {
-    const char *p;
-    unsigned char *q;
-
-    pa_assert(str);
-    pa_assert(data);
-
-    q = data;
-    for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
-        unsigned int val = token_decode(p);
-        unsigned int marker = (val >> 24) & 0xff;
-        if (val == BASE64_DECODE_ERROR)
-            return -1;
-        *q++ = (val >> 16) & 0xff;
-        if (marker < 2)
-            *q++ = (val >> 8) & 0xff;
-        if (marker < 1)
-            *q++ = val & 0xff;
-    }
-
-    return q - (unsigned char *) data;
-}
-
-int pa_raop_md5_hash(const char *data, int len, char **str) {
-    unsigned char d[MD5_DIGEST_LENGTH];
-    char *s = NULL;
-    int i;
-
-    pa_assert(data);
-    pa_assert(str);
-
-    MD5((unsigned char*) data, len, d);
-    s = pa_xnew(char, MD5_HASH_LENGTH);
-    for (i = 0; i < MD5_DIGEST_LENGTH; i++)
-        sprintf(&s[2*i], "%02x", (unsigned int) d[i]);
-
-    *str = s;
-    s[MD5_HASH_LENGTH] = 0;
-    return strlen(s);
-}
-
-int pa_raop_basic_response(const char *user, const char *pwd, char **str) {
-    char *tmp, *B = NULL;
-
-    pa_assert(str);
-
-    tmp = pa_sprintf_malloc("%s:%s", user, pwd);
-    pa_raop_base64_encode(tmp, strlen(tmp), &B);
-    pa_xfree(tmp);
-
-    *str = B;
-    return strlen(B);
-}
-
-int pa_raop_digest_response(const char *user, const char *realm, const char *password,
-                            const char *nonce, const char *uri, char **str) {
-    char *A1, *HA1, *A2, *HA2;
-    char *tmp, *KD = NULL;
-
-    pa_assert(str);
-
-    A1 = pa_sprintf_malloc("%s:%s:%s", user, realm, password);
-    pa_raop_md5_hash(A1, strlen(A1), &HA1);
-    pa_xfree(A1);
-
-    A2 = pa_sprintf_malloc("OPTIONS:%s", uri);
-    pa_raop_md5_hash(A2, strlen(A2), &HA2);
-    pa_xfree(A2);
-
-    tmp = pa_sprintf_malloc("%s:%s:%s", HA1, nonce, HA2);
-    pa_raop_md5_hash(tmp, strlen(tmp), &KD);
-    pa_xfree(tmp);
-
-    pa_xfree(HA1);
-    pa_xfree(HA2);
-
-    *str = KD;
-    return strlen(KD);
-}
diff --git a/src/modules/raop/raop_util.h b/src/modules/raop/raop_util.h
deleted file mode 100644
index d3f7566..0000000
--- a/src/modules/raop/raop_util.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef fooraoputilfoo
-#define fooraoputilfoo
-
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2008 Colin Guthrie
-  Copyright Kungliga Tekniska högskolan
-  Copyright 2013 Martin Blanchard
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  PulseAudio 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 Lesser General Public License
-  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
-***/
-
-/***
-  This file was originally inspired by a file developed by
-  Kungliga Tekniska högskolan.
-***/
-
-int pa_raop_base64_encode(const void *data, int len, char **str);
-int pa_raop_base64_decode(const char *str, void *data);
-
-int pa_raop_md5_hash(const char *data, int len, char **str);
-
-int pa_raop_basic_response(const char *user, const char *pwd, char **str);
-int pa_raop_digest_response(const char *user, const char *realm, const char *password,
-                            const char *nonce, const char *uri, char **str);
-
-#endif
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index c6d9ac6..31b8673 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -58,8 +58,8 @@ struct pa_rtsp_client {
     void *userdata;
     const char *useragent;
 
-    pa_rtsp_state state;
-    pa_rtsp_status status;
+    pa_rtsp_state_t state;
+    pa_rtsp_status_t status;
     uint8_t waiting;
 
     pa_headerlist* headers;
diff --git a/src/modules/rtp/rtsp_client.h b/src/modules/rtp/rtsp_client.h
index 4a35851..4e031d8 100644
--- a/src/modules/rtp/rtsp_client.h
+++ b/src/modules/rtp/rtsp_client.h
@@ -32,7 +32,7 @@
 
 typedef struct pa_rtsp_client pa_rtsp_client;
 
-typedef enum {
+typedef enum pa_rtsp_state {
   STATE_CONNECT,
   STATE_OPTIONS,
   STATE_ANNOUNCE,
@@ -42,17 +42,17 @@ typedef enum {
   STATE_FLUSH,
   STATE_TEARDOWN,
   STATE_DISCONNECTED
-} pa_rtsp_state;
+} pa_rtsp_state_t;
 
-typedef enum {
+typedef enum pa_rtsp_status {
   STATUS_OK             = 200,
   STATUS_BAD_REQUEST    = 400,
   STATUS_UNAUTHORIZED   = 401,
   STATUS_NO_RESPONSE    = 444,
   STATUS_INTERNAL_ERROR = 500
-} pa_rtsp_status;
+} pa_rtsp_status_t;
 
-typedef void (*pa_rtsp_cb_t)(pa_rtsp_client *c, pa_rtsp_state state, pa_rtsp_status code, pa_headerlist *headers, void *userdata);
+typedef void (*pa_rtsp_cb_t)(pa_rtsp_client *c, pa_rtsp_state_t state, pa_rtsp_status_t code, pa_headerlist *headers, void *userdata);
 
 pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char *hostname, uint16_t port, const char *useragent);
 void pa_rtsp_client_free(pa_rtsp_client *c);

commit 5ff21c3bdd0e1873bdb90bf148f706f26bf6360e
Author: Martin Blanchard <tchaik at gmx.com>
Date:   Sun Nov 6 12:54:06 2016 -0600

    raop: Add BA (Basic) and DA (Digest) HTTP authentication helpers
    
    RAOP authentication is using standard HTTP challenge-response authentication
    scheme. This patch adds two helper functions that generate the proper hash
    (for both techniques) given a username, a password and session related tokens.

diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
index a2ec91e..1323cd0 100644
--- a/src/modules/raop/raop_client.c
+++ b/src/modules/raop/raop_client.c
@@ -65,6 +65,9 @@
 #define VOLUME_MIN -144
 #define VOLUME_MAX 0
 
+#define USER_AGENT "iTunes/11.0.4 (Windows; N)"
+#define USER_NAME "iTunes"
+
 #define DEFAULT_RAOP_PORT 5000
 #define UDP_DEFAULT_AUDIO_PORT 6000
 #define UDP_DEFAULT_CONTROL_PORT 6001
@@ -85,13 +88,15 @@ struct pa_raop_client {
     pa_core *core;
     char *host;
     uint16_t port;
-    char *sid;
     pa_rtsp_client *rtsp;
-    pa_raop_protocol_t protocol;
+    char *sci, *sid;
+    char *pwd;
 
     uint8_t jack_type;
     uint8_t jack_status;
 
+    pa_raop_protocol_t protocol;
+
     int encryption; /* Enable encryption? */
     pa_raop_secret *aes;
 
@@ -125,6 +130,9 @@ struct pa_raop_client {
     uint32_t udp_sync_interval;
     uint32_t udp_sync_count;
 
+    pa_raop_client_auth_cb_t udp_auth_callback;
+    void *udp_auth_userdata;
+
     pa_raop_client_setup_cb_t udp_setup_callback;
     void *udp_setup_userdata;
 
@@ -576,7 +584,7 @@ static void do_rtsp_announce(pa_raop_client *c) {
     pa_xfree(sdp);
 }
 
-static void tcp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *userdata) {
+static void tcp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_rtsp_status status, pa_headerlist* headers, void *userdata) {
     pa_raop_client* c = userdata;
     pa_assert(c);
     pa_assert(rtsp);
@@ -675,12 +683,13 @@ static void tcp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist
     }
 }
 
-static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist *headers, void *userdata) {
+static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_rtsp_status status, pa_headerlist *headers, void *userdata) {
     pa_raop_client *c = userdata;
 
     pa_assert(c);
     pa_assert(rtsp);
     pa_assert(rtsp == c->rtsp);
+    pa_assert(STATUS_OK == status);
 
     switch (state) {
         case STATE_CONNECT: {
@@ -982,6 +991,178 @@ static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist
     }
 }
 
+static void rtsp_authentication_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_rtsp_status status, pa_headerlist *headers, void *userdata) {
+    pa_raop_client *c = userdata;
+
+    pa_assert(c);
+    pa_assert(rtsp);
+    pa_assert(rtsp == c->rtsp);
+
+    switch (state) {
+        case STATE_CONNECT: {
+            char *sci = NULL, *sac = NULL;
+            uint16_t rac;
+            struct {
+                uint32_t ci1;
+                uint32_t ci2;
+            } rci;
+
+            pa_random(&rci, sizeof(rci));
+            /* Generate a random Client-Instance number */
+            sci = pa_sprintf_malloc("%08x%08x",rci.ci1, rci.ci2);
+            pa_rtsp_add_header(c->rtsp, "Client-Instance", sci);
+
+            pa_random(&rac, sizeof(rac));
+            /* Generate a random Apple-Challenge key */
+            pa_raop_base64_encode(&rac, 8*sizeof(rac), &sac);
+            pa_log_debug ("APPLECHALLENGE >> %s | %d", sac, (int) sizeof(rac));
+            rtrimchar(sac, '=');
+            pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
+
+            pa_rtsp_options(c->rtsp);
+
+            pa_xfree(sac);
+            pa_xfree(sci);
+            break;
+        }
+
+        case STATE_OPTIONS: {
+            static bool waiting = false;
+            const char *current = NULL;
+            char space[] = " ";
+            char *token,*ath = NULL;
+            char *publ, *wath, *mth, *val;
+            char *realm = NULL, *nonce = NULL, *response = NULL;
+            char comma[] = ",";
+
+            pa_log_debug("RAOP: OPTIONS");
+            /* We do not consider the Apple-Response */
+            pa_rtsp_remove_header(c->rtsp, "Apple-Challenge");
+
+            if (STATUS_UNAUTHORIZED == status) {
+                wath = pa_xstrdup(pa_headerlist_gets(headers, "WWW-Authenticate"));
+                if (true == waiting) {
+                    pa_xfree(wath);
+                    goto failure;
+                }
+
+                if (wath)
+                    mth = pa_split(wath, space, &current);
+                while ((token = pa_split(wath, comma, &current))) {
+                    val = NULL;
+                    if ((val = strstr(token, "="))) {
+                        if (NULL == realm && val > strstr(token, "realm"))
+                            realm = pa_xstrdup(val + 2);
+                        else if (NULL == nonce && val > strstr(token, "nonce"))
+                            nonce = pa_xstrdup(val + 2);
+                        val = NULL;
+                    }
+
+                    pa_xfree(token);
+                }
+
+                if (pa_safe_streq(mth, "Basic")) {
+                    rtrimchar(realm, '\"');
+
+                    pa_raop_basic_response(USER_NAME, c->pwd, &response);
+                    ath = pa_sprintf_malloc("Basic %s",
+                        response);
+
+                    pa_xfree(response);
+                    pa_xfree(realm);
+                } else if (pa_safe_streq(mth, "Digest")) {
+                    rtrimchar(realm, '\"');
+                    rtrimchar(nonce, '\"');
+
+                    pa_raop_digest_response(USER_NAME, realm, c->pwd, nonce, "*", &response);
+                    ath = pa_sprintf_malloc("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"*\", response=\"%s\"",
+                        USER_NAME, realm, nonce,
+                        response);
+
+                    pa_xfree(response);
+                    pa_xfree(realm);
+                    pa_xfree(nonce);
+                } else {
+                    pa_log_error("unsupported authentication method: %s", mth);
+                    pa_xfree(wath);
+                    pa_xfree(mth);
+                    goto error;
+                }
+
+                pa_xfree(wath);
+                pa_xfree(mth);
+
+                pa_rtsp_add_header(c->rtsp, "Authorization", ath);
+                pa_xfree(ath);
+
+                waiting = true;
+                pa_rtsp_options(c->rtsp);
+                break;
+            }
+
+            if (STATUS_OK == status) {
+                publ = pa_xstrdup(pa_headerlist_gets(headers, "Public"));
+                c->sci = pa_xstrdup(pa_rtsp_get_header(c->rtsp, "Client-Instance"));
+
+                if (c->pwd)
+                    pa_xfree(c->pwd);
+                pa_xfree(publ);
+                c->pwd = NULL;
+            }
+
+            if (c->udp_auth_callback)
+                c->udp_auth_callback((int) status, c->udp_auth_userdata);
+            pa_rtsp_client_free(c->rtsp);
+            c->rtsp = NULL;
+
+            waiting = false;
+            break;
+
+        failure:
+            if (c->udp_auth_callback)
+                c->udp_auth_callback((int) STATUS_UNAUTHORIZED, c->udp_auth_userdata);
+            pa_rtsp_client_free(c->rtsp);
+            c->rtsp = NULL;
+
+            pa_log_error("aborting RTSP authentication, wrong password");
+
+            waiting = false;
+            break;
+
+        error:
+            if (c->udp_auth_callback)
+                c->udp_auth_callback((int) status, c->udp_auth_userdata);
+            pa_rtsp_client_free(c->rtsp);
+            c->rtsp = NULL;
+
+            pa_log_error("aborting RTSP authentication, unexpected failure");
+
+            waiting = false;
+            break;
+        }
+
+        case STATE_ANNOUNCE:
+        case STATE_SETUP:
+        case STATE_RECORD:
+        case STATE_SET_PARAMETER:
+        case STATE_FLUSH:
+        case STATE_TEARDOWN:
+        case STATE_DISCONNECTED:
+        default: {
+            if (c->udp_auth_callback)
+                c->udp_auth_callback((int) STATUS_BAD_REQUEST, c->udp_auth_userdata);
+            pa_rtsp_client_free(c->rtsp);
+            c->rtsp = NULL;
+
+            if (c->sci)
+                pa_xfree(c->sci);
+            c->sci = NULL;
+
+            break;
+        }
+    }
+}
+
 pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_protocol_t protocol) {
     pa_raop_client* c;
     pa_parsed_address a;
@@ -1047,15 +1228,40 @@ void pa_raop_client_free(pa_raop_client *c) {
 
     if (c->sid)
         pa_xfree(c->sid);
+    if (c->sci)
+        pa_xfree(c->sci);
+    c->sci = c->sid = NULL;
+
     if (c->aes)
         pa_raop_secret_free(c->aes);
     if (c->rtsp)
         pa_rtsp_client_free(c->rtsp);
-    pa_xfree(c->host);
+    c->rtsp = NULL;
 
+    pa_xfree(c->host);
     pa_xfree(c);
 }
 
+int pa_raop_client_authenticate (pa_raop_client *c, const char *password) {
+
+    pa_assert(c);
+
+    if (c->rtsp || c->pwd) {
+        pa_log_debug("Connection already in progress");
+        return 0;
+    }
+
+    c->pwd = NULL;
+    if (password)
+        c->pwd = pa_xstrdup(password);
+    c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, USER_AGENT);
+
+    pa_assert(c->rtsp);
+
+    pa_rtsp_set_callback(c->rtsp, rtsp_authentication_cb, c);
+    return pa_rtsp_connect(c->rtsp);
+}
+
 int pa_raop_client_connect(pa_raop_client *c) {
     char *sci;
     struct {
@@ -1074,7 +1280,7 @@ int pa_raop_client_connect(pa_raop_client *c) {
     if (c->protocol == RAOP_TCP)
         c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, "iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
     else
-        c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, "iTunes/7.6.2 (Windows; N;)");
+        c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, USER_AGENT);
 
     /* Generate random instance id. */
     pa_random(&rand_data, sizeof(rand_data));
@@ -1116,6 +1322,17 @@ int pa_raop_client_teardown(pa_raop_client *c) {
     return rv;
 }
 
+int pa_raop_client_udp_is_authenticated(pa_raop_client *c) {
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (c->sci != NULL)
+        rv = 1;
+
+    return rv;
+}
+
 int pa_raop_client_udp_is_alive(pa_raop_client *c) {
     int rv = 0;
 
@@ -1147,7 +1364,6 @@ int pa_raop_client_udp_stream(pa_raop_client *c) {
         if (!c->is_recording) {
             c->udp_first_packet = true;
             c->udp_sync_count = 0;
-
             c->is_recording = true;
          }
 
@@ -1326,6 +1542,14 @@ ssize_t pa_raop_client_udp_send_audio_packet(pa_raop_client *c, pa_memchunk *blo
     return len;
 }
 
+void pa_raop_client_set_encryption(pa_raop_client *c, int encryption) {
+    pa_assert(c);
+
+    c->encryption = encryption;
+    if (c->encryption)
+        c->aes = pa_raop_secret_new();
+}
+
 /* Adjust volume so that it fits into VOLUME_DEF <= v <= 0 dB */
 pa_volume_t pa_raop_client_adjust_volume(pa_raop_client *c, pa_volume_t volume) {
     double minv, maxv;
@@ -1469,12 +1693,11 @@ void pa_raop_client_tcp_set_closed_callback(pa_raop_client *c, pa_raop_client_cl
     c->tcp_closed_userdata = userdata;
 }
 
-void pa_raop_client_set_encryption(pa_raop_client *c, int encryption) {
+void pa_raop_client_udp_set_auth_callback(pa_raop_client *c, pa_raop_client_auth_cb_t callback, void *userdata) {
     pa_assert(c);
 
-    c->encryption = encryption;
-    if (c->encryption)
-        c->aes = pa_raop_secret_new();
+    c->udp_auth_callback = callback;
+    c->udp_auth_userdata = userdata;
 }
 
 void pa_raop_client_udp_set_setup_callback(pa_raop_client *c, pa_raop_client_setup_cb_t callback, void *userdata) {
diff --git a/src/modules/raop/raop_client.h b/src/modules/raop/raop_client.h
index 5d0bb14..e208349 100644
--- a/src/modules/raop/raop_client.h
+++ b/src/modules/raop/raop_client.h
@@ -37,10 +37,12 @@ typedef struct pa_raop_client pa_raop_client;
 pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_protocol_t protocol);
 void pa_raop_client_free(pa_raop_client *c);
 
+int pa_raop_client_authenticate (pa_raop_client *c, const char *password);
 int pa_raop_client_connect(pa_raop_client *c);
 int pa_raop_client_flush(pa_raop_client *c);
 int pa_raop_client_teardown(pa_raop_client *c);
 
+int pa_raop_client_udp_is_authenticated(pa_raop_client *c);
 int pa_raop_client_udp_is_alive(pa_raop_client *c);
 int pa_raop_client_udp_can_stream(pa_raop_client *c);
 int pa_raop_client_udp_stream(pa_raop_client *c);
@@ -61,6 +63,8 @@ void pa_raop_client_tcp_set_callback(pa_raop_client *c, pa_raop_client_cb_t call
 typedef void (*pa_raop_client_closed_cb_t)(void *userdata);
 void pa_raop_client_tcp_set_closed_callback(pa_raop_client *c, pa_raop_client_closed_cb_t callback, void *userdata);
 
+typedef void (*pa_raop_client_auth_cb_t)(int status, void *userdata);
+void pa_raop_client_udp_set_auth_callback(pa_raop_client *c, pa_raop_client_auth_cb_t callback, void *userdata);
 
 typedef void (*pa_raop_client_setup_cb_t)(int control_fd, int timing_fd, void *userdata);
 void pa_raop_client_udp_set_setup_callback(pa_raop_client *c, pa_raop_client_setup_cb_t callback, void *userdata);
diff --git a/src/modules/raop/raop_util.c b/src/modules/raop/raop_util.c
index c9021ec..709ec27 100644
--- a/src/modules/raop/raop_util.c
+++ b/src/modules/raop/raop_util.c
@@ -35,6 +35,7 @@
 
 #include <pulse/xmalloc.h>
 
+#include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
 
 #include "raop_util.h"
@@ -168,3 +169,42 @@ int pa_raop_md5_hash(const char *data, int len, char **str) {
     s[MD5_HASH_LENGTH] = 0;
     return strlen(s);
 }
+
+int pa_raop_basic_response(const char *user, const char *pwd, char **str) {
+    char *tmp, *B = NULL;
+
+    pa_assert(str);
+
+    tmp = pa_sprintf_malloc("%s:%s", user, pwd);
+    pa_raop_base64_encode(tmp, strlen(tmp), &B);
+    pa_xfree(tmp);
+
+    *str = B;
+    return strlen(B);
+}
+
+int pa_raop_digest_response(const char *user, const char *realm, const char *password,
+                            const char *nonce, const char *uri, char **str) {
+    char *A1, *HA1, *A2, *HA2;
+    char *tmp, *KD = NULL;
+
+    pa_assert(str);
+
+    A1 = pa_sprintf_malloc("%s:%s:%s", user, realm, password);
+    pa_raop_md5_hash(A1, strlen(A1), &HA1);
+    pa_xfree(A1);
+
+    A2 = pa_sprintf_malloc("OPTIONS:%s", uri);
+    pa_raop_md5_hash(A2, strlen(A2), &HA2);
+    pa_xfree(A2);
+
+    tmp = pa_sprintf_malloc("%s:%s:%s", HA1, nonce, HA2);
+    pa_raop_md5_hash(tmp, strlen(tmp), &KD);
+    pa_xfree(tmp);
+
+    pa_xfree(HA1);
+    pa_xfree(HA2);
+
+    *str = KD;
+    return strlen(KD);
+}
diff --git a/src/modules/raop/raop_util.h b/src/modules/raop/raop_util.h
index dc0b767..d3f7566 100644
--- a/src/modules/raop/raop_util.h
+++ b/src/modules/raop/raop_util.h
@@ -32,4 +32,8 @@ int pa_raop_base64_decode(const char *str, void *data);
 
 int pa_raop_md5_hash(const char *data, int len, char **str);
 
+int pa_raop_basic_response(const char *user, const char *pwd, char **str);
+int pa_raop_digest_response(const char *user, const char *realm, const char *password,
+                            const char *nonce, const char *uri, char **str);
+
 #endif
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index 198417e..c6d9ac6 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -59,6 +59,7 @@ struct pa_rtsp_client {
     const char *useragent;
 
     pa_rtsp_state state;
+    pa_rtsp_status status;
     uint8_t waiting;
 
     pa_headerlist* headers;
@@ -119,8 +120,8 @@ void pa_rtsp_client_free(pa_rtsp_client *c) {
 }
 
 static void headers_read(pa_rtsp_client *c) {
-    char* token;
     char delimiters[] = ";";
+    char* token;
 
     pa_assert(c);
     pa_assert(c->response_headers);
@@ -165,14 +166,14 @@ static void headers_read(pa_rtsp_client *c) {
     }
 
     /* Call our callback */
-    c->callback(c, c->state, c->response_headers, c->userdata);
+    c->callback(c, c->state, c->status, c->response_headers, c->userdata);
 }
 
 static void line_callback(pa_ioline *line, const char *s, void *userdata) {
+    pa_rtsp_client *c = userdata;
     char *delimpos;
     char *s2, *s2p;
 
-    pa_rtsp_client *c = userdata;
     pa_assert(line);
     pa_assert(c);
     pa_assert(c->callback);
@@ -180,7 +181,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
     if (!s) {
         /* Keep the ioline/iochannel open as they will be freed automatically */
         c->ioline = NULL;
-        c->callback(c, STATE_DISCONNECTED, NULL, c->userdata);
+        c->callback(c, STATE_DISCONNECTED, STATUS_NO_RESPONSE, NULL, c->userdata);
         return;
     }
 
@@ -191,17 +192,35 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
         *s2p = '\0';
         s2p -= 1;
     }
+
     if (c->waiting && pa_streq(s2, "RTSP/1.0 200 OK")) {
+        if (c->response_headers)
+            pa_headerlist_free(c->response_headers);
+        c->response_headers = pa_headerlist_new();
+
+        c->status = STATUS_OK;
         c->waiting = 0;
+        goto exit;
+    } else if (c->waiting && pa_streq(s2, "RTSP/1.0 401 Unauthorized")) {
         if (c->response_headers)
             pa_headerlist_free(c->response_headers);
         c->response_headers = pa_headerlist_new();
+
+        c->status = STATUS_UNAUTHORIZED;
+        c->waiting = 0;
         goto exit;
-    }
-    if (c->waiting) {
-        pa_log_warn("Unexpected response: %s", s2);
+    } else if (c->waiting) {
+        pa_log_warn("Unexpected/Unhandled response: %s", s2);
+
+        if (pa_streq(s2, "RTSP/1.0 400 Bad Request"))
+            c->status = STATUS_BAD_REQUEST;
+        else if (pa_streq(s2, "RTSP/1.0 500 Internal Server Error"))
+            c->status = STATUS_INTERNAL_ERROR;
+        else
+            c->status = STATUS_NO_RESPONSE;
         goto exit;
     }
+
     if (!strlen(s2)) {
         /* End of headers */
         /* We will have a header left from our looping iteration, so add it in :) */
@@ -228,7 +247,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
     if (c->last_header && ' ' == s2[0]) {
         pa_assert(c->header_buffer);
 
-        /* Add this line to the buffer (sans the space. */
+        /* Add this line to the buffer (sans the space) */
         pa_strbuf_puts(c->header_buffer, &(s2[1]));
         goto exit;
     }
@@ -270,6 +289,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
 
     /* Save the header name */
     c->last_header = pa_xstrdup(s2);
+
   exit:
     pa_xfree(s2);
 }
@@ -317,7 +337,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
     pa_log_debug("Established RTSP connection from local ip %s", c->localip);
 
     if (c->callback)
-        c->callback(c, c->state, NULL, c->userdata);
+        c->callback(c, c->state, STATUS_OK, NULL, c->userdata);
 }
 
 int pa_rtsp_connect(pa_rtsp_client *c) {
@@ -336,6 +356,7 @@ int pa_rtsp_connect(pa_rtsp_client *c) {
     pa_socket_client_set_callback(c->sc, on_connection, c);
     c->waiting = 1;
     c->state = STATE_CONNECT;
+    c->status = STATUS_NO_RESPONSE;
     return 0;
 }
 
@@ -368,12 +389,25 @@ uint32_t pa_rtsp_serverport(pa_rtsp_client *c) {
     return c->rtp_port;
 }
 
+bool pa_rtsp_exec_ready(const pa_rtsp_client *c) {
+    pa_assert(c);
+
+    return c->url != NULL && c->ioline != NULL;
+}
+
 void pa_rtsp_set_url(pa_rtsp_client *c, const char *url) {
     pa_assert(c);
 
     c->url = pa_xstrdup(url);
 }
 
+bool pa_rtsp_has_header(pa_rtsp_client *c, const char *key) {
+    pa_assert(c);
+    pa_assert(key);
+
+    return pa_headerlist_contains(c->headers, key);
+}
+
 void pa_rtsp_add_header(pa_rtsp_client *c, const char *key, const char *value) {
     pa_assert(c);
     pa_assert(key);
@@ -382,17 +416,18 @@ void pa_rtsp_add_header(pa_rtsp_client *c, const char *key, const char *value) {
     pa_headerlist_puts(c->headers, key, value);
 }
 
-void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key) {
+const char* pa_rtsp_get_header(pa_rtsp_client *c, const char *key) {
     pa_assert(c);
     pa_assert(key);
 
-    pa_headerlist_remove(c->headers, key);
+    return pa_headerlist_gets(c->headers, key);
 }
 
-bool pa_rtsp_exec_ready(const pa_rtsp_client *c) {
+void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key) {
     pa_assert(c);
+    pa_assert(key);
 
-    return c->url != NULL && c->ioline != NULL;
+    pa_headerlist_remove(c->headers, key);
 }
 
 static int rtsp_exec(pa_rtsp_client *c, const char *cmd,
@@ -527,17 +562,6 @@ int pa_rtsp_record(pa_rtsp_client *c, uint16_t *seq, uint32_t *rtptime) {
     return rv;
 }
 
-int pa_rtsp_teardown(pa_rtsp_client *c) {
-    int rv;
-
-    pa_assert(c);
-
-    c->state = STATE_TEARDOWN;
-    rv = rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
-
-    return rv;
-}
-
 int pa_rtsp_setparameter(pa_rtsp_client *c, const char *param) {
     int rv;
 
@@ -570,3 +594,14 @@ int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime) {
     pa_headerlist_free(headers);
     return rv;
 }
+
+int pa_rtsp_teardown(pa_rtsp_client *c) {
+    int rv;
+
+    pa_assert(c);
+
+    c->state = STATE_TEARDOWN;
+    rv = rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
+
+    return rv;
+}
diff --git a/src/modules/rtp/rtsp_client.h b/src/modules/rtp/rtsp_client.h
index abc60ee..4a35851 100644
--- a/src/modules/rtp/rtsp_client.h
+++ b/src/modules/rtp/rtsp_client.h
@@ -31,43 +31,53 @@
 #include "headerlist.h"
 
 typedef struct pa_rtsp_client pa_rtsp_client;
+
 typedef enum {
   STATE_CONNECT,
   STATE_OPTIONS,
   STATE_ANNOUNCE,
   STATE_SETUP,
   STATE_RECORD,
+  STATE_SET_PARAMETER,
   STATE_FLUSH,
   STATE_TEARDOWN,
-  STATE_SET_PARAMETER,
   STATE_DISCONNECTED
 } pa_rtsp_state;
-typedef void (*pa_rtsp_cb_t)(pa_rtsp_client *c, pa_rtsp_state state, pa_headerlist *hl, void *userdata);
+
+typedef enum {
+  STATUS_OK             = 200,
+  STATUS_BAD_REQUEST    = 400,
+  STATUS_UNAUTHORIZED   = 401,
+  STATUS_NO_RESPONSE    = 444,
+  STATUS_INTERNAL_ERROR = 500
+} pa_rtsp_status;
+
+typedef void (*pa_rtsp_cb_t)(pa_rtsp_client *c, pa_rtsp_state state, pa_rtsp_status code, pa_headerlist *headers, void *userdata);
 
 pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char *hostname, uint16_t port, const char *useragent);
 void pa_rtsp_client_free(pa_rtsp_client *c);
 
 int pa_rtsp_connect(pa_rtsp_client *c);
 void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userdata);
-
 void pa_rtsp_disconnect(pa_rtsp_client *c);
 
 const char* pa_rtsp_localip(pa_rtsp_client *c);
 uint32_t pa_rtsp_serverport(pa_rtsp_client *c);
+bool pa_rtsp_exec_ready(const pa_rtsp_client *c);
+
 void pa_rtsp_set_url(pa_rtsp_client *c, const char *url);
+
+bool pa_rtsp_has_header(pa_rtsp_client *c, const char *key);
 void pa_rtsp_add_header(pa_rtsp_client *c, const char *key, const char *value);
+const char* pa_rtsp_get_header(pa_rtsp_client *c, const char *key);
 void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key);
 
-bool pa_rtsp_exec_ready(const pa_rtsp_client *c);
-
 int pa_rtsp_options(pa_rtsp_client *c);
 int pa_rtsp_announce(pa_rtsp_client *c, const char *sdp);
-
 int pa_rtsp_setup(pa_rtsp_client *c, const char *transport);
 int pa_rtsp_record(pa_rtsp_client *c, uint16_t *seq, uint32_t *rtptime);
-int pa_rtsp_teardown(pa_rtsp_client *c);
-
 int pa_rtsp_setparameter(pa_rtsp_client *c, const char *param);
 int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime);
+int pa_rtsp_teardown(pa_rtsp_client *c);
 
 #endif

commit a33c04c0cc82e7c4aef640801e39651bcb9342b6
Author: Martin Blanchard <tchaik at gmx.com>
Date:   Sun Nov 6 12:54:05 2016 -0600

    raop: Add a MD5 hashing fuction
    
    MD5 hashing will be needed during the authentication process.
    
    Original patch by Martin Blanchard. Patch splitted by
    Hajime Fujita <crisp.fujita at nifty.com>.

diff --git a/src/modules/raop/raop_util.c b/src/modules/raop/raop_util.c
index d24d67d..c9021ec 100644
--- a/src/modules/raop/raop_util.c
+++ b/src/modules/raop/raop_util.c
@@ -30,12 +30,21 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <openssl/err.h>
+#include <openssl/md5.h>
+
 #include <pulse/xmalloc.h>
 
 #include <pulsecore/macro.h>
 
 #include "raop_util.h"
 
+#ifndef MD5_DIGEST_LENGTH
+#define MD5_DIGEST_LENGTH 16
+#endif
+
+#define MD5_HASH_LENGTH (2*MD5_DIGEST_LENGTH)
+
 #define BASE64_DECODE_ERROR 0xffffffff
 
 static const char base64_chars[] =
@@ -141,3 +150,21 @@ int pa_raop_base64_decode(const char *str, void *data) {
 
     return q - (unsigned char *) data;
 }
+
+int pa_raop_md5_hash(const char *data, int len, char **str) {
+    unsigned char d[MD5_DIGEST_LENGTH];
+    char *s = NULL;
+    int i;
+
+    pa_assert(data);
+    pa_assert(str);
+
+    MD5((unsigned char*) data, len, d);
+    s = pa_xnew(char, MD5_HASH_LENGTH);
+    for (i = 0; i < MD5_DIGEST_LENGTH; i++)
+        sprintf(&s[2*i], "%02x", (unsigned int) d[i]);
+
+    *str = s;
+    s[MD5_HASH_LENGTH] = 0;
+    return strlen(s);
+}
diff --git a/src/modules/raop/raop_util.h b/src/modules/raop/raop_util.h
index 7a8d73e..dc0b767 100644
--- a/src/modules/raop/raop_util.h
+++ b/src/modules/raop/raop_util.h
@@ -30,4 +30,6 @@
 int pa_raop_base64_encode(const void *data, int len, char **str);
 int pa_raop_base64_decode(const char *str, void *data);
 
+int pa_raop_md5_hash(const char *data, int len, char **str);
+
 #endif

commit bac8a2ba66e0194f539a5e0fdca99f8c6c70a9c2
Author: Martin Blanchard <tchaik at gmx.com>
Date:   Sun Nov 6 12:54:04 2016 -0600

    raop: Move base64 implementation to a util file
    
    Base64 implementation is now in a common file called raop_util.c.
    Old Base64 files are removed but copyright is preserved.
    
    Original patch by Martin Blanchard, patch splitted by
    Hajime Fujita <crisp.fujita at nifty.com>.

diff --git a/src/Makefile.am b/src/Makefile.am
index ebd727e..c3ddc86 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1155,9 +1155,9 @@ librtp_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINOR@.la libpulsecommon-@
 
 libraop_la_SOURCES = \
         modules/raop/raop_client.c modules/raop/raop_client.h \
+        modules/raop/raop_packet_buffer.h modules/raop/raop_packet_buffer.c \
         modules/raop/raop_crypto.c modules/raop/raop_crypto.h \
-        modules/raop/base64.c modules/raop/base64.h \
-        modules/raop/raop_packet_buffer.h modules/raop/raop_packet_buffer.c
+        modules/raop/raop_util.c modules/raop/raop_util.h
 libraop_la_CFLAGS = $(AM_CFLAGS) $(OPENSSL_CFLAGS) -I$(top_srcdir)/src/modules/rtp
 libraop_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
 libraop_la_LIBADD = $(AM_LIBADD) $(OPENSSL_LIBS) libpulsecore- at PA_MAJORMINOR@.la librtp.la libpulsecommon- at PA_MAJORMINOR@.la libpulse.la
diff --git a/src/modules/raop/base64.c b/src/modules/raop/base64.c
deleted file mode 100644
index 073e6a2..0000000
--- a/src/modules/raop/base64.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2008 Colin Guthrie
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  PulseAudio 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 Lesser General Public License
-  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
-***/
-
-/***
-  This file was originally inspired by a file developed by
-  Kungliga Tekniska högskolan
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <pulse/xmalloc.h>
-
-#include "base64.h"
-
-static const char base64_chars[] =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-static int pos(char c) {
-    if (c >= 'A' && c <= 'Z')
-        return c - 'A' + 0;
-    if (c >= 'a' && c <= 'z')
-        return c - 'a' + 26;
-    if (c >= '0' && c <= '9')
-        return c - '0' + 52;
-    if (c == '+')
-        return 62;
-    if (c == '/')
-        return 63;
-
-    return -1;
-}
-
-int pa_base64_encode(const void *data, int size, char **str) {
-    char *s, *p;
-    int i;
-    int c;
-    const unsigned char *q;
-
-    p = s = pa_xnew(char, size * 4 / 3 + 4);
-    q = (const unsigned char *) data;
-    for (i = 0; i < size;) {
-        c = q[i++];
-        c *= 256;
-        if (i < size)
-            c += q[i];
-        i++;
-        c *= 256;
-        if (i < size)
-            c += q[i];
-        i++;
-        p[0] = base64_chars[(c & 0x00fc0000) >> 18];
-        p[1] = base64_chars[(c & 0x0003f000) >> 12];
-        p[2] = base64_chars[(c & 0x00000fc0) >> 6];
-        p[3] = base64_chars[(c & 0x0000003f) >> 0];
-        if (i > size)
-            p[3] = '=';
-        if (i > size + 1)
-            p[2] = '=';
-        p += 4;
-    }
-
-    *p = 0;
-    *str = s;
-
-    return strlen(s);
-}
-
-#define DECODE_ERROR 0xffffffff
-
-static unsigned int token_decode(const char *token) {
-    int i;
-    unsigned int val = 0;
-    int marker = 0;
-
-    if (strlen(token) < 4)
-        return DECODE_ERROR;
-    for (i = 0; i < 4; i++) {
-        val *= 64;
-        if (token[i] == '=')
-            marker++;
-        else if (marker > 0)
-            return DECODE_ERROR;
-        else {
-            int lpos = pos(token[i]);
-            if (lpos < 0)
-                return DECODE_ERROR;
-            val += lpos;
-        }
-    }
-
-    if (marker > 2)
-        return DECODE_ERROR;
-
-    return (marker << 24) | val;
-}
-
-int pa_base64_decode(const char *str, void *data) {
-    const char *p;
-    unsigned char *q;
-
-    q = data;
-    for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
-        unsigned int val = token_decode(p);
-        unsigned int marker = (val >> 24) & 0xff;
-        if (val == DECODE_ERROR)
-            return -1;
-        *q++ = (val >> 16) & 0xff;
-        if (marker < 2)
-            *q++ = (val >> 8) & 0xff;
-        if (marker < 1)
-            *q++ = val & 0xff;
-    }
-
-    return q - (unsigned char *) data;
-}
diff --git a/src/modules/raop/base64.h b/src/modules/raop/base64.h
deleted file mode 100644
index 478fa4d..0000000
--- a/src/modules/raop/base64.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef foobase64hfoo
-#define foobase64hfoo
-
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2008 Colin Guthrie
-  Copyright Kungliga Tekniska högskolan
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  PulseAudio 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 Lesser General Public License
-  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
-***/
-
-/***
-  This file was originally inspired by a file developed by
-  Kungliga Tekniska högskolan
-***/
-
-int pa_base64_encode(const void *data, int size, char **str);
-int pa_base64_decode(const char *str, void *data);
-
-#endif
diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
index 5c825eb..a2ec91e 100644
--- a/src/modules/raop/raop_client.c
+++ b/src/modules/raop/raop_client.c
@@ -53,7 +53,7 @@
 #include "rtsp_client.h"
 #include "raop_packet_buffer.h"
 #include "raop_crypto.h"
-#include "base64.h"
+#include "raop_util.h"
 
 #define JACK_STATUS_DISCONNECTED 0
 #define JACK_STATUS_CONNECTED 1
@@ -528,7 +528,7 @@ static void do_rtsp_announce(pa_raop_client *c) {
     /* UDP protocol does not need "Apple-Challenge" at announce. */
     if (c->protocol == RAOP_TCP) {
         pa_random(&rand_data, sizeof(rand_data));
-        pa_base64_encode(&rand_data, sizeof(rand_data), &sac);
+        pa_raop_base64_encode(&rand_data, 8*sizeof(rand_data), &sac);
         rtrimchar(sac, '=');
         pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
     }
@@ -689,7 +689,7 @@ static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist
 
             /* Set the Apple-Challenge key */
             pa_random(&rand, sizeof(rand));
-            pa_base64_encode(&rand, sizeof(rand), &sac);
+            pa_raop_base64_encode(&rand, 8*sizeof(rand), &sac);
             rtrimchar(sac, '=');
             pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
 
diff --git a/src/modules/raop/raop_crypto.c b/src/modules/raop/raop_crypto.c
index 6cbb840..4d08e26 100644
--- a/src/modules/raop/raop_crypto.c
+++ b/src/modules/raop/raop_crypto.c
@@ -37,7 +37,7 @@
 #include <pulsecore/random.h>
 
 #include "raop_crypto.h"
-#include "base64.h"
+#include "raop_util.h"
 
 #define AES_CHUNK_SIZE 16
 
@@ -85,9 +85,9 @@ static int rsa_encrypt(uint8_t *data, int len, uint8_t *str) {
     pa_assert(str);
 
     rsa = RSA_new();
-    size = pa_base64_decode(rsa_modulus, modules);
+    size = pa_raop_base64_decode(rsa_modulus, modules);
     n_bn = BN_bin2bn(modules, size, NULL);
-    size = pa_base64_decode(rsa_exponent, exponent);
+    size = pa_raop_base64_decode(rsa_exponent, exponent);
     e_bn = BN_bin2bn(exponent, size, NULL);
     RSA_set0_key(rsa, n_bn, e_bn, NULL);
 
@@ -120,7 +120,7 @@ char* pa_raop_secret_get_iv(pa_raop_secret *s) {
 
     pa_assert(s);
 
-    pa_base64_encode(s->iv, AES_CHUNK_SIZE, &base64_iv);
+    pa_raop_base64_encode(s->iv, AES_CHUNK_SIZE, &base64_iv);
 
     return base64_iv;
 }
@@ -134,7 +134,7 @@ char* pa_raop_secret_get_key(pa_raop_secret *s) {
 
     /* Encrypt our AES public key to send to the device */
     size = rsa_encrypt(s->key, AES_CHUNK_SIZE, rsa_key);
-    pa_base64_encode(rsa_key, size, &base64_key);
+    pa_raop_base64_encode(rsa_key, size, &base64_key);
 
     return base64_key;
 }
diff --git a/src/modules/raop/raop_util.c b/src/modules/raop/raop_util.c
new file mode 100644
index 0000000..d24d67d
--- /dev/null
+++ b/src/modules/raop/raop_util.c
@@ -0,0 +1,143 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Martin Blanchard
+  Copyright Kungliga Tekniska Høgskolan & Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio 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 Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/***
+  The base64 implementation was originally inspired by a file developed
+  by Kungliga Tekniska högskolan.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+
+#include "raop_util.h"
+
+#define BASE64_DECODE_ERROR 0xffffffff
+
+static const char base64_chars[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int char_position(char c) {
+    if (c >= 'A' && c <= 'Z')
+        return c - 'A' + 0;
+    if (c >= 'a' && c <= 'z')
+        return c - 'a' + 26;
+    if (c >= '0' && c <= '9')
+        return c - '0' + 52;
+    if (c == '+')
+        return 62;
+    if (c == '/')
+        return 63;
+
+    return -1;
+}
+
+static unsigned int token_decode(const char *token) {
+    unsigned int val = 0;
+    int marker = 0;
+    int i;
+
+    if (strlen(token) < 4)
+        return BASE64_DECODE_ERROR;
+    for (i = 0; i < 4; i++) {
+        val *= 64;
+        if (token[i] == '=')
+            marker++;
+        else if (marker > 0)
+            return BASE64_DECODE_ERROR;
+        else {
+            int lpos = char_position(token[i]);
+            if (lpos < 0)
+                return BASE64_DECODE_ERROR;
+            val += lpos;
+        }
+    }
+
+    if (marker > 2)
+        return BASE64_DECODE_ERROR;
+
+    return (marker << 24) | val;
+}
+
+int pa_raop_base64_encode(const void *data, int len, char **str) {
+    const unsigned char *q;
+    char *p, *s = NULL;
+    int i, c;
+
+    pa_assert(data);
+    pa_assert(str);
+
+    p = s = pa_xnew(char, len * 4 / 3 + 4);
+    q = (const unsigned char *) data;
+    for (i = 0; i < len;) {
+        c = q[i++];
+        c *= 256;
+        if (i < len)
+            c += q[i];
+        i++;
+        c *= 256;
+        if (i < len)
+            c += q[i];
+        i++;
+        p[0] = base64_chars[(c & 0x00fc0000) >> 18];
+        p[1] = base64_chars[(c & 0x0003f000) >> 12];
+        p[2] = base64_chars[(c & 0x00000fc0) >> 6];
+        p[3] = base64_chars[(c & 0x0000003f) >> 0];
+        if (i > len)
+            p[3] = '=';
+        if (i > len + 1)
+            p[2] = '=';
+        p += 4;
+    }
+
+    *p = 0;
+    *str = s;
+    return strlen(s);
+}
+
+int pa_raop_base64_decode(const char *str, void *data) {
+    const char *p;
+    unsigned char *q;
+
+    pa_assert(str);
+    pa_assert(data);
+
+    q = data;
+    for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
+        unsigned int val = token_decode(p);
+        unsigned int marker = (val >> 24) & 0xff;
+        if (val == BASE64_DECODE_ERROR)
+            return -1;
+        *q++ = (val >> 16) & 0xff;
+        if (marker < 2)
+            *q++ = (val >> 8) & 0xff;
+        if (marker < 1)
+            *q++ = val & 0xff;
+    }
+
+    return q - (unsigned char *) data;
+}
diff --git a/src/modules/raop/raop_util.h b/src/modules/raop/raop_util.h
new file mode 100644
index 0000000..7a8d73e
--- /dev/null
+++ b/src/modules/raop/raop_util.h
@@ -0,0 +1,33 @@
+#ifndef fooraoputilfoo
+#define fooraoputilfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Colin Guthrie
+  Copyright Kungliga Tekniska högskolan
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio 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 Lesser General Public License
+  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/***
+  This file was originally inspired by a file developed by
+  Kungliga Tekniska högskolan.
+***/
+
+int pa_raop_base64_encode(const void *data, int len, char **str);
+int pa_raop_base64_decode(const char *str, void *data);
+
+#endif

commit 31ded701b1d3bd9f2880a7f20da601dce62af3ba
Author: Martin Blanchard <tchaik at gmx.com>
Date:   Sun Nov 6 12:54:03 2016 -0600

    raop: Extract encryption related code into a separate file
    
    That makes the raop_client.c code smaller/cleaner and will simplify
    addition of more crypto related stuffs like authentication.

diff --git a/src/Makefile.am b/src/Makefile.am
index ebc633c..ebd727e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1155,6 +1155,7 @@ librtp_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINOR@.la libpulsecommon-@
 
 libraop_la_SOURCES = \
         modules/raop/raop_client.c modules/raop/raop_client.h \
+        modules/raop/raop_crypto.c modules/raop/raop_crypto.h \
         modules/raop/base64.c modules/raop/base64.h \
         modules/raop/raop_packet_buffer.h modules/raop/raop_packet_buffer.c
 libraop_la_CFLAGS = $(AM_CFLAGS) $(OPENSSL_CFLAGS) -I$(top_srcdir)/src/modules/rtp
diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
index 16a59cc..5c825eb 100644
--- a/src/modules/raop/raop_client.c
+++ b/src/modules/raop/raop_client.c
@@ -32,13 +32,6 @@
 #include <sys/filio.h>
 #endif
 
-/* TODO: Replace OpenSSL with NSS */
-#include <openssl/err.h>
-#include <openssl/rand.h>
-#include <openssl/aes.h>
-#include <openssl/rsa.h>
-#include <openssl/engine.h>
-
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
 #include <pulse/sample.h>
@@ -56,12 +49,11 @@
 #include <pulsecore/random.h>
 
 #include "raop_client.h"
-#include "rtsp_client.h"
-#include "base64.h"
 
+#include "rtsp_client.h"
 #include "raop_packet_buffer.h"
-
-#define AES_CHUNKSIZE 16
+#include "raop_crypto.h"
+#include "base64.h"
 
 #define JACK_STATUS_DISCONNECTED 0
 #define JACK_STATUS_CONNECTED 1
@@ -89,21 +81,6 @@ typedef enum {
     UDP_PAYLOAD_AUDIO_DATA = 0x60
 } pa_raop_udp_payload_type;
 
-/* Openssl 1.1.0 broke compatibility. Before 1.1.0 we had to set RSA->n and
- * RSA->e manually, but after 1.1.0 the RSA struct is opaque and we have to use
- * RSA_set0_key(). RSA_set0_key() is a new function added in 1.1.0. We could
- * depend on openssl 1.1.0, but it may take some time before distributions will
- * be able to upgrade to the new openssl version. To insulate ourselves from
- * such transition problems, let's implement RSA_set0_key() ourselves if it's
- * not available. */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-static int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) {
-    r->n = n;
-    r->e = e;
-    return 1;
-}
-#endif
-
 struct pa_raop_client {
     pa_core *core;
     char *host;
@@ -115,12 +92,8 @@ struct pa_raop_client {
     uint8_t jack_type;
     uint8_t jack_status;
 
-    /* Encryption Related bits */
     int encryption; /* Enable encryption? */
-    AES_KEY aes;
-    uint8_t aes_iv[AES_CHUNKSIZE]; /* Initialization vector for aes-cbc */
-    uint8_t aes_nv[AES_CHUNKSIZE]; /* Next vector for aes-cbc */
-    uint8_t aes_key[AES_CHUNKSIZE]; /* Key for aes-cbc */
+    pa_raop_secret *aes;
 
     uint16_t seq;
     uint32_t rtptime;
@@ -255,53 +228,6 @@ static inline void bit_writer(uint8_t **buffer, uint8_t *bit_pos, int *size, uin
     }
 }
 
-static int rsa_encrypt(uint8_t *text, int len, uint8_t *res) {
-    const char n[] =
-        "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC"
-        "5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDR"
-        "KSKv6kDqnw4UwPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuB"
-        "OitnZ/bDzPHrTOZz0Dew0uowxf/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJ"
-        "Q+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/UAaHqn9JdsBWLUEpVviYnh"
-        "imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew==";
-    const char e[] = "AQAB";
-    uint8_t modules[256];
-    uint8_t exponent[8];
-    int size;
-    RSA *rsa;
-    BIGNUM *n_bn;
-    BIGNUM *e_bn;
-
-    rsa = RSA_new();
-    size = pa_base64_decode(n, modules);
-    n_bn = BN_bin2bn(modules, size, NULL);
-    size = pa_base64_decode(e, exponent);
-    e_bn = BN_bin2bn(exponent, size, NULL);
-    RSA_set0_key(rsa, n_bn, e_bn, NULL);
-
-    size = RSA_public_encrypt(len, text, res, rsa, RSA_PKCS1_OAEP_PADDING);
-    RSA_free(rsa);
-    return size;
-}
-
-static int aes_encrypt(pa_raop_client *c, uint8_t *data, int size) {
-    uint8_t *buf;
-    int i=0, j;
-
-    pa_assert(c);
-
-    memcpy(c->aes_nv, c->aes_iv, AES_CHUNKSIZE);
-    while (i+AES_CHUNKSIZE <= size) {
-        buf = data + i;
-        for (j=0; j<AES_CHUNKSIZE; ++j)
-            buf[j] ^= c->aes_nv[j];
-
-        AES_encrypt(buf, buf, &c->aes);
-        memcpy(c->aes_nv, buf, AES_CHUNKSIZE);
-        i += AES_CHUNKSIZE;
-    }
-    return i;
-}
-
 static inline void rtrimchar(char *str, char rc) {
     char *sp = str + strlen(str) - 1;
     while (sp >= str && *sp == rc) {
@@ -588,8 +514,6 @@ static ssize_t udp_send_audio_packet(pa_raop_client *c, bool retrans, uint8_t *b
 }
 
 static void do_rtsp_announce(pa_raop_client *c) {
-    int i;
-    uint8_t rsakey[512];
     char *key, *iv, *sac = NULL, *sdp;
     uint16_t rand_data;
     const char *ip;
@@ -601,22 +525,20 @@ static void do_rtsp_announce(pa_raop_client *c) {
     pa_rtsp_set_url(c->rtsp, url);
     pa_xfree(url);
 
-    /* Now encrypt our aes_public key to send to the device. */
-    i = rsa_encrypt(c->aes_key, AES_CHUNKSIZE, rsakey);
-    pa_base64_encode(rsakey, i, &key);
-    rtrimchar(key, '=');
-    pa_base64_encode(c->aes_iv, AES_CHUNKSIZE, &iv);
-    rtrimchar(iv, '=');
-
     /* UDP protocol does not need "Apple-Challenge" at announce. */
     if (c->protocol == RAOP_TCP) {
         pa_random(&rand_data, sizeof(rand_data));
-        pa_base64_encode(&rand_data, AES_CHUNKSIZE, &sac);
+        pa_base64_encode(&rand_data, sizeof(rand_data), &sac);
         rtrimchar(sac, '=');
         pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
     }
 
-    if (c->encryption)
+    if (c->encryption) {
+        iv = pa_raop_secret_get_iv(c->aes);
+        rtrimchar(iv, '=');
+        key = pa_raop_secret_get_key(c->aes);
+        rtrimchar(key, '=');
+
         sdp = pa_sprintf_malloc(
             "v=0\r\n"
             "o=iTunes %s 0 IN IP4 %s\r\n"
@@ -631,7 +553,10 @@ static void do_rtsp_announce(pa_raop_client *c) {
             c->sid, ip, c->host,
             c->protocol == RAOP_TCP ? 4096 : UDP_FRAMES_PER_PACKET,
             key, iv);
-    else
+
+        pa_xfree(iv);
+        pa_xfree(key);
+    } else {
         sdp = pa_sprintf_malloc(
             "v=0\r\n"
             "o=iTunes %s 0 IN IP4 %s\r\n"
@@ -643,10 +568,10 @@ static void do_rtsp_announce(pa_raop_client *c) {
             "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 44100\r\n",
             c->sid, ip, c->host,
             c->protocol == RAOP_TCP ? 4096 : UDP_FRAMES_PER_PACKET);
+    }
 
     pa_rtsp_announce(c->rtsp, sdp);
-    pa_xfree(key);
-    pa_xfree(iv);
+
     pa_xfree(sac);
     pa_xfree(sdp);
 }
@@ -764,7 +689,7 @@ static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist
 
             /* Set the Apple-Challenge key */
             pa_random(&rand, sizeof(rand));
-            pa_base64_encode(&rand, AES_CHUNKSIZE, &sac);
+            pa_base64_encode(&rand, sizeof(rand), &sac);
             rtrimchar(sac, '=');
             pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
 
@@ -1081,6 +1006,9 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_prot
     c->udp_control_fd = -1;
     c->udp_timing_fd = -1;
 
+    c->encryption = 0;
+    c->aes = NULL;
+
     c->udp_my_control_port     = UDP_DEFAULT_CONTROL_PORT;
     c->udp_server_control_port = UDP_DEFAULT_CONTROL_PORT;
     c->udp_my_timing_port      = UDP_DEFAULT_TIMING_PORT;
@@ -1116,11 +1044,15 @@ void pa_raop_client_free(pa_raop_client *c) {
     pa_assert(c);
 
     pa_raop_pb_delete(c->packet_buffer);
-    if (c->rtsp)
-        pa_rtsp_client_free(c->rtsp);
+
     if (c->sid)
         pa_xfree(c->sid);
+    if (c->aes)
+        pa_raop_secret_free(c->aes);
+    if (c->rtsp)
+        pa_rtsp_client_free(c->rtsp);
     pa_xfree(c->host);
+
     pa_xfree(c);
 }
 
@@ -1144,12 +1076,6 @@ int pa_raop_client_connect(pa_raop_client *c) {
     else
         c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, "iTunes/7.6.2 (Windows; N;)");
 
-    /* Initialise the AES encryption system. */
-    pa_random(c->aes_iv, sizeof(c->aes_iv));
-    pa_random(c->aes_key, sizeof(c->aes_key));
-    memcpy(c->aes_nv, c->aes_iv, sizeof(c->aes_nv));
-    AES_set_encrypt_key(c->aes_key, 128, &c->aes);
-
     /* Generate random instance id. */
     pa_random(&rand_data, sizeof(rand_data));
     c->sid = pa_sprintf_malloc("%u", rand_data.a);
@@ -1520,7 +1446,7 @@ int pa_raop_client_encode_sample(pa_raop_client *c, pa_memchunk *raw, pa_memchun
 
     if (c->encryption) {
         /* Encrypt our data. */
-        aes_encrypt(c, (b + header_size), size);
+        pa_raop_aes_encrypt(c->aes, (b + header_size), size);
     }
 
     /* We're done with the chunk. */
@@ -1544,7 +1470,11 @@ void pa_raop_client_tcp_set_closed_callback(pa_raop_client *c, pa_raop_client_cl
 }
 
 void pa_raop_client_set_encryption(pa_raop_client *c, int encryption) {
+    pa_assert(c);
+
     c->encryption = encryption;
+    if (c->encryption)
+        c->aes = pa_raop_secret_new();
 }
 
 void pa_raop_client_udp_set_setup_callback(pa_raop_client *c, pa_raop_client_setup_cb_t callback, void *userdata) {
diff --git a/src/modules/raop/raop_client.h b/src/modules/raop/raop_client.h
index d49c146..5d0bb14 100644
--- a/src/modules/raop/raop_client.h
+++ b/src/modules/raop/raop_client.h
@@ -50,8 +50,7 @@ pa_volume_t pa_raop_client_adjust_volume(pa_raop_client *c, pa_volume_t volume);
 int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume);
 int pa_raop_client_encode_sample(pa_raop_client *c, pa_memchunk *raw, pa_memchunk *encoded);
 
-int pa_raop_client_udp_handle_timing_packet(pa_raop_client *c, const uint8_t packet
-[], ssize_t size);
+int pa_raop_client_udp_handle_timing_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size);
 int pa_raop_client_udp_handle_control_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size);
 int pa_raop_client_udp_get_blocks_size(pa_raop_client *c, size_t *size);
 ssize_t pa_raop_client_udp_send_audio_packet(pa_raop_client *c, pa_memchunk *block);
diff --git a/src/modules/raop/raop_crypto.c b/src/modules/raop/raop_crypto.c
new file mode 100644
index 0000000..6cbb840
--- /dev/null
+++ b/src/modules/raop/raop_crypto.c
@@ -0,0 +1,164 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio 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 Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <openssl/err.h>
+#include <openssl/aes.h>
+#include <openssl/rsa.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/random.h>
+
+#include "raop_crypto.h"
+#include "base64.h"
+
+#define AES_CHUNK_SIZE 16
+
+/* Openssl 1.1.0 broke compatibility. Before 1.1.0 we had to set RSA->n and
+ * RSA->e manually, but after 1.1.0 the RSA struct is opaque and we have to use
+ * RSA_set0_key(). RSA_set0_key() is a new function added in 1.1.0. We could
+ * depend on openssl 1.1.0, but it may take some time before distributions will
+ * be able to upgrade to the new openssl version. To insulate ourselves from
+ * such transition problems, let's implement RSA_set0_key() ourselves if it's
+ * not available. */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) {
+    r->n = n;
+    r->e = e;
+    return 1;
+}
+#endif
+
+struct pa_raop_secret {
+    uint8_t key[AES_CHUNK_SIZE]; /* Key for aes-cbc */
+    uint8_t iv[AES_CHUNK_SIZE];  /* Initialization vector for cbc */
+    AES_KEY aes;                 /* AES encryption */
+};
+
+static const char rsa_modulus[] =
+    "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC"
+    "5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDR"
+    "KSKv6kDqnw4UwPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuB"
+    "OitnZ/bDzPHrTOZz0Dew0uowxf/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJ"
+    "Q+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/UAaHqn9JdsBWLUEpVviYnh"
+    "imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew==";
+
+static const char rsa_exponent[] =
+    "AQAB";
+
+static int rsa_encrypt(uint8_t *data, int len, uint8_t *str) {
+    uint8_t modules[256];
+    uint8_t exponent[8];
+    int size;
+    RSA *rsa;
+    BIGNUM *n_bn;
+    BIGNUM *e_bn;
+
+    pa_assert(data);
+    pa_assert(str);
+
+    rsa = RSA_new();
+    size = pa_base64_decode(rsa_modulus, modules);
+    n_bn = BN_bin2bn(modules, size, NULL);
+    size = pa_base64_decode(rsa_exponent, exponent);
+    e_bn = BN_bin2bn(exponent, size, NULL);
+    RSA_set0_key(rsa, n_bn, e_bn, NULL);
+
+    size = RSA_public_encrypt(len, data, str, rsa, RSA_PKCS1_OAEP_PADDING);
+
+    RSA_free(rsa);
+    return size;
+}
+
+pa_raop_secret* pa_raop_secret_new(void) {
+    pa_raop_secret *s = pa_xnew0(pa_raop_secret, 1);
+
+    pa_assert(s);
+
+    pa_random(s->key, sizeof(s->key));
+    AES_set_encrypt_key(s->key, 128, &s->aes);
+    pa_random(s->iv, sizeof(s->iv));
+
+    return s;
+}
+
+void pa_raop_secret_free(pa_raop_secret *s) {
+    pa_assert(s);
+
+    pa_xfree(s);
+}
+
+char* pa_raop_secret_get_iv(pa_raop_secret *s) {
+    char *base64_iv = NULL;
+
+    pa_assert(s);
+
+    pa_base64_encode(s->iv, AES_CHUNK_SIZE, &base64_iv);
+
+    return base64_iv;
+}
+
+char* pa_raop_secret_get_key(pa_raop_secret *s) {
+    char *base64_key = NULL;
+    uint8_t rsa_key[512];
+    int size = 0;
+
+    pa_assert(s);
+
+    /* Encrypt our AES public key to send to the device */
+    size = rsa_encrypt(s->key, AES_CHUNK_SIZE, rsa_key);
+    pa_base64_encode(rsa_key, size, &base64_key);
+
+    return base64_key;
+}
+
+int pa_raop_aes_encrypt(pa_raop_secret *s, uint8_t *data, int len) {
+    static uint8_t nv[AES_CHUNK_SIZE];
+    uint8_t *buffer;
+    int i = 0, j;
+
+    pa_assert(s);
+    pa_assert(data);
+
+    memcpy(nv, s->iv, AES_CHUNK_SIZE);
+
+    while (i + AES_CHUNK_SIZE <= len) {
+        buffer = data + i;
+        for (j = 0; j < AES_CHUNK_SIZE; ++j)
+            buffer[j] ^= nv[j];
+
+        AES_encrypt(buffer, buffer, &s->aes);
+
+        memcpy(nv, buffer, AES_CHUNK_SIZE);
+        i += AES_CHUNK_SIZE;
+    }
+
+    return i;
+}
diff --git a/src/modules/raop/raop_crypto.h b/src/modules/raop/raop_crypto.h
new file mode 100644
index 0000000..65f7577
--- /dev/null
+++ b/src/modules/raop/raop_crypto.h
@@ -0,0 +1,35 @@
+#ifndef fooraopcryptofoo
+#define fooraopcryptofoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2013 Martin Blanchard
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio 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 Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+typedef struct pa_raop_secret pa_raop_secret;
+
+pa_raop_secret* pa_raop_secret_new(void);
+void pa_raop_secret_free(pa_raop_secret *s);
+
+char* pa_raop_secret_get_iv(pa_raop_secret *s);
+char* pa_raop_secret_get_key(pa_raop_secret *s);
+
+int pa_raop_aes_encrypt(pa_raop_secret *s, uint8_t *data, int len);
+
+#endif

commit 31e2bc2fcf828e399e62e1525e1f4fc959475106
Author: Martin Blanchard <tchaik at gmx.com>
Date:   Sun Nov 6 12:54:02 2016 -0600

    raop: Better playback resume handling
    
    When playback stops, a FLUSH command is send to the server and the sink
    goes to IDLE. If playback resumes quickly, sink goes back to RUNNING
    (without being SUSPENDED) and the sink should just start streaming again.
    This patch implements this behaviour.

diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c
index a8a466d..e251f2f 100644
--- a/src/modules/raop/module-raop-sink.c
+++ b/src/modules/raop/module-raop-sink.c
@@ -332,10 +332,14 @@ static int udp_sink_process_msg(pa_msgobject *o, int code, void *data, int64_t o
 
                     pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
 
-                    if (!pa_raop_client_udp_can_stream(u->raop)) {
-                        /* Connecting will trigger a RECORD */
+                    if (!pa_raop_client_udp_is_alive(u->raop)) {
+                        /* Connecting will trigger a RECORD and start steaming */
                         pa_raop_client_connect(u->raop);
+                    } else if (!pa_raop_client_udp_can_stream(u->raop)) {
+                        /* RECORD alredy sent, simply start streaming */
+                        pa_raop_client_udp_stream(u->raop);
                     }
+
                     udp_start_wakeup_clock(u);
 
                     break;
diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
index d17cc44..16a59cc 100644
--- a/src/modules/raop/raop_client.c
+++ b/src/modules/raop/raop_client.c
@@ -952,8 +952,6 @@ static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist
 
             pa_log_debug("RAOP: RECORD");
 
-            c->is_recording = true;
-
             alt = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Latency"));
             /* Generate a random synchronization source identifier from this session. */
             pa_random(&rand, sizeof(rand));
@@ -965,6 +963,8 @@ static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist
             c->udp_first_packet = true;
             c->udp_sync_count = 0;
 
+            c->is_recording = true;
+
             c->udp_record_callback(c->udp_setup_userdata);
 
             pa_xfree(alt);
@@ -1168,11 +1168,12 @@ int pa_raop_client_connect(pa_raop_client *c) {
 
 int pa_raop_client_flush(pa_raop_client *c) {
     int rv = 0;
+
     pa_assert(c);
 
     if (c->rtsp != NULL) {
         rv = pa_rtsp_flush(c->rtsp, c->seq, c->rtptime);
-        c->udp_sync_count = -1;
+        c->udp_sync_count = 0;
     }
 
     return rv;
@@ -1211,6 +1212,25 @@ int pa_raop_client_udp_can_stream(pa_raop_client *c) {
     return rv;
 }
 
+int pa_raop_client_udp_stream(pa_raop_client *c) {
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (c->rtsp != NULL && c->udp_stream_fd > 0) {
+        if (!c->is_recording) {
+            c->udp_first_packet = true;
+            c->udp_sync_count = 0;
+
+            c->is_recording = true;
+         }
+
+        rv = 1;
+    }
+
+    return rv;
+}
+
 int pa_raop_client_udp_handle_timing_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size) {
     const uint32_t * data = NULL;
     uint8_t payload = 0;
diff --git a/src/modules/raop/raop_client.h b/src/modules/raop/raop_client.h
index 6ab6d32..d49c146 100644
--- a/src/modules/raop/raop_client.h
+++ b/src/modules/raop/raop_client.h
@@ -43,6 +43,7 @@ int pa_raop_client_teardown(pa_raop_client *c);
 
 int pa_raop_client_udp_is_alive(pa_raop_client *c);
 int pa_raop_client_udp_can_stream(pa_raop_client *c);
+int pa_raop_client_udp_stream(pa_raop_client *c);
 
 void pa_raop_client_set_encryption(pa_raop_client *c, int encryption);
 pa_volume_t pa_raop_client_adjust_volume(pa_raop_client *c, pa_volume_t volume);

commit 604bf450dc200ee15a8cb1158aa8130caa5b9e2e
Author: Martin Blanchard <tchaik at gmx.com>
Date:   Sun Nov 6 12:54:01 2016 -0600

    raop: Do not send audio before RECORD response
    
    This patch prevents audio packets to be sent before the server
    respond to the RECORD command.

diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c
index ad19cad..a8a466d 100644
--- a/src/modules/raop/module-raop-sink.c
+++ b/src/modules/raop/module-raop-sink.c
@@ -310,7 +310,7 @@ static int udp_sink_process_msg(pa_msgobject *o, int code, void *data, int64_t o
                     pa_log_debug("RAOP: SUSPENDED");
                     pa_smoother_pause(u->smoother, pa_rtclock_now());
 
-                    if (pa_raop_client_udp_can_stream(u->raop)) {
+                    if (pa_raop_client_udp_is_alive(u->raop)) {
                         /* Issue a TEARDOWN if we are still connected. */
                         pa_raop_client_teardown(u->raop);
                     }
@@ -756,10 +756,10 @@ static void udp_thread_func(struct userdata *u) {
             continue;
         }
 
-        if (!pa_raop_client_udp_can_stream(u->raop))
-            continue;
         if (u->sink->thread_info.state != PA_SINK_RUNNING)
             continue;
+        if (!pa_raop_client_udp_can_stream(u->raop))
+            continue;
 
         if (u->encoded_memchunk.length <= 0) {
             if (u->encoded_memchunk.memblock != NULL)
diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
index 9c31162..d17cc44 100644
--- a/src/modules/raop/raop_client.c
+++ b/src/modules/raop/raop_client.c
@@ -146,6 +146,8 @@ struct pa_raop_client {
 
     uint32_t udp_ssrc;
 
+    bool is_recording;
+
     bool udp_first_packet;
     uint32_t udp_sync_interval;
     uint32_t udp_sync_count;
@@ -950,6 +952,8 @@ static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist
 
             pa_log_debug("RAOP: RECORD");
 
+            c->is_recording = true;
+
             alt = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Latency"));
             /* Generate a random synchronization source identifier from this session. */
             pa_random(&rand, sizeof(rand));
@@ -976,6 +980,8 @@ static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist
         case STATE_FLUSH: {
             pa_log_debug("RAOP: FLUSHED");
 
+            c->is_recording = false;
+
             break;
         }
 
@@ -984,6 +990,8 @@ static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist
             pa_assert(c->udp_disconnected_callback);
             pa_assert(c->rtsp);
 
+            c->is_recording = false;
+
             pa_rtsp_disconnect(c->rtsp);
 
             if (c->udp_stream_fd > 0) {
@@ -1084,6 +1092,8 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_prot
     else
         c->port = DEFAULT_RAOP_PORT;
 
+    c->is_recording = false;
+
     c->udp_first_packet = true;
 
     ss = core->default_sample_spec;
@@ -1151,6 +1161,8 @@ int pa_raop_client_connect(pa_raop_client *c) {
     else
         pa_rtsp_set_callback(c->rtsp, udp_rtsp_cb, c);
 
+    c->is_recording = false;
+
     return pa_rtsp_connect(c->rtsp);
 }
 
@@ -1177,7 +1189,7 @@ int pa_raop_client_teardown(pa_raop_client *c) {
     return rv;
 }
 
-int pa_raop_client_udp_can_stream(pa_raop_client *c) {
+int pa_raop_client_udp_is_alive(pa_raop_client *c) {
     int rv = 0;
 
     pa_assert(c);
@@ -1188,6 +1200,17 @@ int pa_raop_client_udp_can_stream(pa_raop_client *c) {
     return rv;
 }
 
+int pa_raop_client_udp_can_stream(pa_raop_client *c) {
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (c->is_recording && c->udp_stream_fd > 0)
+        rv = 1;
+
+    return rv;
+}
+
 int pa_raop_client_udp_handle_timing_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size) {
     const uint32_t * data = NULL;
     uint8_t payload = 0;
diff --git a/src/modules/raop/raop_client.h b/src/modules/raop/raop_client.h
index 578e9d0..6ab6d32 100644
--- a/src/modules/raop/raop_client.h
+++ b/src/modules/raop/raop_client.h
@@ -41,6 +41,7 @@ int pa_raop_client_connect(pa_raop_client *c);
 int pa_raop_client_flush(pa_raop_client *c);
 int pa_raop_client_teardown(pa_raop_client *c);
 
+int pa_raop_client_udp_is_alive(pa_raop_client *c);
 int pa_raop_client_udp_can_stream(pa_raop_client *c);
 
 void pa_raop_client_set_encryption(pa_raop_client *c, int encryption);

commit 29385da2393c2cce5a39ca44493ca37937027592
Author: Matthias Wabersich <pulseaudio at niafc.de>
Date:   Sun Nov 6 12:54:00 2016 -0600

    raop: Packet retransmission support for UDP
    
    This patch adds an RTP audio packet retransmission support and a
    circular buffer implementation for it.
    
    This patch was originally written by Matthias Wabersich [1] and
    later debugged and integrated into the latest tree by Hajime Fujita
    
    [1]: https://bugs.freedesktop.org/show_bug.cgi?id=42804#c44

diff --git a/src/Makefile.am b/src/Makefile.am
index 5584728..ebc633c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1155,7 +1155,8 @@ librtp_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINOR@.la libpulsecommon-@
 
 libraop_la_SOURCES = \
         modules/raop/raop_client.c modules/raop/raop_client.h \
-        modules/raop/base64.c modules/raop/base64.h
+        modules/raop/base64.c modules/raop/base64.h \
+        modules/raop/raop_packet_buffer.h modules/raop/raop_packet_buffer.c
 libraop_la_CFLAGS = $(AM_CFLAGS) $(OPENSSL_CFLAGS) -I$(top_srcdir)/src/modules/rtp
 libraop_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
 libraop_la_LIBADD = $(AM_LIBADD) $(OPENSSL_LIBS) libpulsecore- at PA_MAJORMINOR@.la librtp.la libpulsecommon- at PA_MAJORMINOR@.la libpulse.la
diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
index 4e8f474..9c31162 100644
--- a/src/modules/raop/raop_client.c
+++ b/src/modules/raop/raop_client.c
@@ -59,7 +59,8 @@
 #include "rtsp_client.h"
 #include "base64.h"
 
-#define UDP_FRAMES_PER_PACKET 352
+#include "raop_packet_buffer.h"
+
 #define AES_CHUNKSIZE 16
 
 #define JACK_STATUS_DISCONNECTED 0
@@ -77,6 +78,8 @@
 #define UDP_DEFAULT_CONTROL_PORT 6001
 #define UDP_DEFAULT_TIMING_PORT 6002
 
+#define UDP_DEFAULT_PKT_BUF_SIZE 1000
+
 typedef enum {
     UDP_PAYLOAD_TIMING_REQUEST = 0x52,
     UDP_PAYLOAD_TIMING_RESPONSE = 0x53,
@@ -155,6 +158,8 @@ struct pa_raop_client {
 
     pa_raop_client_disconnected_cb_t udp_disconnected_callback;
     void *udp_disconnected_userdata;
+
+    pa_raop_packet_buffer *packet_buffer;
 };
 
 /* Timming packet header (8x8):
@@ -548,12 +553,35 @@ static void udp_build_audio_header(pa_raop_client *c, uint32_t *buffer, size_t s
     buffer[2] = htonl(c->udp_ssrc);
 }
 
-static ssize_t udp_send_audio_packet(pa_raop_client *c, uint8_t *buffer, size_t size) {
-    ssize_t length;
+/* Audio retransmission header:
+ * [0]    RTP v2: 0x80
+ * [1]    Payload type: 0x56 + 0x80 (marker == on)
+ * [2]    Unknown; seems always 0x01
+ * [3]    Unknown; seems some random number around 0x20~0x40
+ * [4,5]  Original RTP header htons(0x8060)
+ * [6,7]  Packet sequence number to be retransmitted
+ * [8,11] Original RTP timestamp on the lost packet */
+static void udp_build_retrans_header(uint32_t *buffer, size_t size, uint16_t seq_num) {
+    uint8_t x = 0x30; /* FIXME: what's this?? */
+
+    pa_assert(size >= sizeof(uint32_t) * 2);
+
+    buffer[0] = htonl((uint32_t) 0x80000000
+                      | ((uint32_t) UDP_PAYLOAD_RETRANSMIT_REPLY | 0x80) << 16
+                      | 0x0100
+                      | x);
+    buffer[1] = htonl((uint32_t) 0x80600000 | seq_num);
+}
 
-    length = pa_write(c->udp_stream_fd, buffer, size, NULL);
-    c->seq++;
+static ssize_t udp_send_audio_packet(pa_raop_client *c, bool retrans, uint8_t *buffer, size_t size) {
+    ssize_t length;
+    int fd = retrans ? c->udp_control_fd : c->udp_stream_fd;
 
+    length = pa_write(fd, buffer, size, NULL);
+    if (length < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+        pa_log_debug("Discarding audio packet %d due to EAGAIN", c->seq);
+        length = size;
+    }
     return length;
 }
 
@@ -965,6 +993,8 @@ static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist
 
             pa_log_debug("RTSP control channel closed (teardown)");
 
+            pa_raop_pb_clear(c->packet_buffer);
+
             pa_rtsp_client_free(c->rtsp);
             pa_xfree(c->sid);
             c->rtsp = NULL;
@@ -1001,6 +1031,8 @@ static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist
 
             pa_log_debug("RTSP control channel closed (disconnected)");
 
+            pa_raop_pb_clear(c->packet_buffer);
+
             pa_rtsp_client_free(c->rtsp);
             pa_xfree(c->sid);
             c->rtsp = NULL;
@@ -1064,7 +1096,8 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_prot
             pa_raop_client_free(c);
             return NULL;
         }
-    }
+    } else
+        c->packet_buffer = pa_raop_pb_new(UDP_DEFAULT_PKT_BUF_SIZE);
 
     return c;
 }
@@ -1072,6 +1105,7 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_prot
 void pa_raop_client_free(pa_raop_client *c) {
     pa_assert(c);
 
+    pa_raop_pb_delete(c->packet_buffer);
     if (c->rtsp)
         pa_rtsp_client_free(c->rtsp);
     if (c->sid)
@@ -1188,14 +1222,48 @@ int pa_raop_client_udp_handle_timing_packet(pa_raop_client *c, const uint8_t pac
     return rv;
 }
 
+static int udp_resend_packets(pa_raop_client *c, uint16_t seq_num, uint16_t num_packets) {
+    int rv = -1;
+    uint8_t *data = NULL;
+    ssize_t len = 0;
+    int i = 0;
+
+    pa_assert(c);
+    pa_assert(num_packets > 0);
+    pa_assert(c->packet_buffer);
+
+    for (i = seq_num; i < seq_num + num_packets; i++) {
+        len = pa_raop_pb_read_packet(c->packet_buffer, i, (uint8_t **) &data);
+
+        if (len > 0) {
+            ssize_t r;
+
+            /* Obtained buffer has a header room for retransmission
+               header */
+            udp_build_retrans_header((uint32_t *) data, len, seq_num);
+            r = udp_send_audio_packet(c, true /* retrans */, data, len);
+            if (r == len)
+                rv = 0;
+            else
+                rv = -1;
+        } else
+            pa_log_debug("Packet not found in retrans buffer: %u", i);
+    }
+
+    return rv;
+}
+
 int pa_raop_client_udp_handle_control_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size) {
     uint8_t payload = 0;
     int rv = 0;
 
+    uint16_t seq_num;
+    uint16_t num_packets;
+
     pa_assert(c);
     pa_assert(packet);
 
-    if (size != 20 || packet[0] != 0x80)
+    if ((size != 20 && size != 8) || packet[0] != 0x80)
     {
         pa_log_debug("Received an invalid control packet.");
         return 1;
@@ -1206,12 +1274,24 @@ int pa_raop_client_udp_handle_control_packet(pa_raop_client *c, const uint8_t pa
     payload = packet[1] ^ 0x80;
     switch (payload) {
         case UDP_PAYLOAD_RETRANSMIT_REQUEST:
-            /* Packet retransmission not implemented yet... */
-            /* rv = ... */
+            pa_assert(size == 8);
+
+            /* Requested start sequence number */
+            seq_num = ((uint16_t) packet[4]) << 8;
+            seq_num |= (uint16_t) packet[5];
+            /* Number of requested packets starting at requested seq. number */
+            num_packets = (uint16_t) packet[6] << 8;
+            num_packets |= (uint16_t) packet[7];
+            pa_log_debug("Resending %d packets starting at %d", num_packets, seq_num);
+            rv = udp_resend_packets(c, seq_num, num_packets);
             break;
+
         case UDP_PAYLOAD_RETRANSMIT_REPLY:
+            pa_log_debug("Received a retransmit reply packet on control port (this should never happen)");
+            break;
+
         default:
-            pa_log_debug("Got an unexpected payload type on control channel !");
+            pa_log_debug("Got an unexpected payload type on control channel: %u !", payload);
             return 1;
     }
 
@@ -1248,7 +1328,14 @@ ssize_t pa_raop_client_udp_send_audio_packet(pa_raop_client *c, pa_memchunk *blo
     pa_assert(buf);
     pa_assert(block->length > 0);
     udp_build_audio_header(c, (uint32_t *) (buf + block->index), block->length);
-    len = udp_send_audio_packet(c, buf + block->index, block->length);
+    len = udp_send_audio_packet(c, false, buf + block->index, block->length);
+
+    /* Store packet for resending in the packet buffer */
+    pa_raop_pb_write_packet(c->packet_buffer, c->seq, buf + block->index,
+                            block->length);
+
+    c->seq++;
+
     pa_memblock_release(block->memblock);
 
     if (len > 0) {
diff --git a/src/modules/raop/raop_client.h b/src/modules/raop/raop_client.h
index 36be8dc..578e9d0 100644
--- a/src/modules/raop/raop_client.h
+++ b/src/modules/raop/raop_client.h
@@ -25,6 +25,8 @@
 #include <pulsecore/core.h>
 #include <pulsecore/memchunk.h>
 
+#define UDP_FRAMES_PER_PACKET 352
+
 typedef enum pa_raop_protocol {
     RAOP_TCP,
     RAOP_UDP,
diff --git a/src/modules/raop/raop_packet_buffer.c b/src/modules/raop/raop_packet_buffer.c
new file mode 100644
index 0000000..b8c1bc8
--- /dev/null
+++ b/src/modules/raop/raop_packet_buffer.c
@@ -0,0 +1,172 @@
+/***
+  Circular buffer for RTP audio packets with random access support
+  by RTP sequence number.
+
+  Copyright 2013 Matthias Wabersich, Hajime Fujita
+
+  This is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  This 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 Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+
+***/
+
+#include <stdlib.h>
+#include <limits.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-error.h>
+#include "raop_client.h"
+
+#include "raop_packet_buffer.h"
+
+/* FRAMES_PER_PACKET*2*2 + sizeof(udp_audio_header) + sizeof(ALAC header), unencoded */
+#define PACKET_SIZE_MAX (UDP_FRAMES_PER_PACKET*2*2 + 12 + 7)
+/* Header room for packet retransmission header */
+#define RETRANS_HEADER_ROOM 4
+
+/* Packet element */
+struct pa_raop_packet_element {
+    uint16_t  seq_num; /* RTP sequence number (in host byte order) */
+    ssize_t   length;  /* Actual packet length */
+    /* Packet data including RTP header */
+    uint8_t   data[PACKET_SIZE_MAX + RETRANS_HEADER_ROOM];
+};
+
+/* Buffer struct */
+struct pa_raop_packet_buffer {
+    size_t   size;          /* max number of packets in buffer */
+    size_t   start;         /* index of oldest packet */
+    size_t   count;         /* number of packets in buffer */
+    uint16_t first_seq_num; /* Sequence number of first packet in buffer */
+    uint16_t latest_seq_num; /* Debug purpose */
+    pa_raop_packet_element *packets; /* Packet element pointer */
+};
+
+pa_raop_packet_buffer *pa_raop_pb_new(size_t size) {
+    pa_raop_packet_buffer *pb = pa_xmalloc0(sizeof(*pb));
+
+    pb->size = size;
+    pb->packets = (pa_raop_packet_element *)
+        pa_xmalloc(size * sizeof(pa_raop_packet_element));
+
+    pa_raop_pb_clear(pb);
+
+    return pb;
+}
+
+void pa_raop_pb_clear(pa_raop_packet_buffer *pb) {
+    pb->start = 0;
+    pb->count = 0;
+    pb->first_seq_num = 0;
+    pb->latest_seq_num = 0;
+    memset(pb->packets, 0, pb->size * sizeof(pa_raop_packet_element));
+}
+
+void pa_raop_pb_delete(pa_raop_packet_buffer *pb) {
+    pa_xfree(pb->packets);
+    pa_xfree(pb);
+}
+
+static int pb_is_full(pa_raop_packet_buffer *pb) {
+    return pb->count == pb->size;
+}
+
+static int pb_is_empty(pa_raop_packet_buffer *pb) {
+    return pb->count == 0;
+}
+
+static pa_raop_packet_element *pb_prepare_write(pa_raop_packet_buffer *pb, uint16_t seq) {
+    size_t end = (pb->start + pb->count) % pb->size;
+    pa_raop_packet_element *packet;
+
+    /* Set first packet sequence number in buffer if buffer is empty */
+    if (pb_is_empty(pb))
+        pb->first_seq_num = seq;
+    else
+        pa_assert((uint16_t) (pb->latest_seq_num + 1) == seq);
+
+    packet = &pb->packets[end];
+
+    if (pb_is_full(pb)) {
+        pb->start = (pb->start + 1) % pb->size; /* full, overwrite */
+
+        /* Set first packet sequence number in buffer
+           to new start packet sequence number */
+        pb->first_seq_num = pb->packets[pb->start].seq_num;
+    } else
+        ++ pb->count;
+
+    pb->latest_seq_num = seq;
+
+    return packet;
+}
+
+/* Write packet data to packet buffer */
+void pa_raop_pb_write_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, const uint8_t *packet_data, ssize_t packet_length) {
+    pa_raop_packet_element *packet;
+
+    pa_assert(pb);
+    pa_assert(packet_data);
+    pa_assert(packet_length <= PACKET_SIZE_MAX);
+
+    packet = pb_prepare_write(pb, seq_num);
+    packet->seq_num = seq_num;
+    packet->length = packet_length + RETRANS_HEADER_ROOM;
+
+    /* Insert RETRANS_HEADER_ROOM bytes in front of packet data,
+       for retransmission header */
+    memset(packet->data, 0, RETRANS_HEADER_ROOM);
+    memcpy(packet->data + RETRANS_HEADER_ROOM, packet_data, packet_length);
+}
+
+/* l < r?, considers wrapping */
+static bool seq_lt(uint16_t l, uint16_t r) {
+    return l - r > USHRT_MAX/2;
+}
+
+/* Random access to packet from buffer by sequence number for (re-)sending. */
+ssize_t pa_raop_pb_read_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, uint8_t **packet_data) {
+    uint16_t index = 0; /* Index of requested packet */
+    pa_raop_packet_element *packet;
+
+    /* If the buffer is empty, there is no use in calculating indices */
+    if (pb_is_empty(pb))
+        return -1;
+
+    /* If the requested packet is too old (seq_num below first seq number
+       in buffer) or too young (seq_num greater than current seq number),
+       do nothing and return */
+    if (seq_lt(seq_num, pb->first_seq_num))
+        return -1;
+
+    index = (uint16_t) (seq_num - pb->first_seq_num);
+    if (index >= pb->count)
+        return -1;
+
+    /*  Index of the requested packet in the buffer is calculated
+        using the first sequence number stored in the buffer.
+        The offset (seq_num - first_seq_num) is used to access the array. */
+    packet = &pb->packets[(pb->start + index) % pb->size];
+
+    pa_assert(packet->data[RETRANS_HEADER_ROOM + 2] == (seq_num >> 8));
+    pa_assert(packet->data[RETRANS_HEADER_ROOM + 3] == (seq_num & 0xff));
+    pa_assert(packet_data);
+
+    *packet_data = packet->data;
+
+    return packet->length;
+}
diff --git a/src/modules/raop/raop_packet_buffer.h b/src/modules/raop/raop_packet_buffer.h
new file mode 100644
index 0000000..d8a08a0
--- /dev/null
+++ b/src/modules/raop/raop_packet_buffer.h
@@ -0,0 +1,42 @@
+#ifndef RAOP_PACKET_BUFFER_H_INCLUDED
+#define RAOP_PACKET_BUFFER_H_INCLUDED
+
+/***
+  Circular buffer for RTP audio packets with random access support
+  by RTP sequence number.
+
+  Copyright 2013 Matthias Wabersich, Hajime Fujita
+
+  This is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  This 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 Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+
+***/
+
+struct pa_raop_packet_element;
+typedef struct pa_raop_packet_element pa_raop_packet_element;
+
+struct pa_raop_packet_buffer;
+typedef struct pa_raop_packet_buffer pa_raop_packet_buffer;
+
+/* Allocates a new circular packet buffer
+   size: Maximum number of packets to store */
+pa_raop_packet_buffer *pa_raop_pb_new(size_t size);
+void pa_raop_pb_clear(pa_raop_packet_buffer *pb);
+void pa_raop_pb_delete(pa_raop_packet_buffer *pb);
+
+void pa_raop_pb_write_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, const uint8_t *packet_data, ssize_t packet_length);
+ssize_t pa_raop_pb_read_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, uint8_t **packet_data);
+
+#endif /* RAOP_PACKET_BUFFER_H_INCLUDED */

commit 402bb7ce482314f75e23ed42687a4f9b42ff602e
Author: Martin Blanchard <tinram at gmx.fr>
Date:   Sun Nov 6 12:53:59 2016 -0600

    raop: Parse server capabilities on discovery
    
    During the discovery phase, raop servers send their capabilities
    (supported encryption, audio codec...). These should be passed to the
    raop sink via module's arguments.
    
    Original patch written by Martin Blanchard, then modified by Hajime
    Fujita <crisp.fujita at nifty.com> based on review comments by
    Anton Lundin <glance at acc.umu.se>.
    Now resolver_cb always dtrdup()s string blocks given by Avahi,
    to make the code easier to maintain.

diff --git a/src/modules/raop/module-raop-discover.c b/src/modules/raop/module-raop-discover.c
index 1ced777..6e2542a 100644
--- a/src/modules/raop/module-raop-discover.c
+++ b/src/modules/raop/module-raop-discover.c
@@ -139,6 +139,9 @@ static void resolver_cb(
     struct userdata *u = userdata;
     struct tunnel *tnl;
     char *device = NULL, *nicename, *dname, *vname, *args;
+    char *tp = NULL, *et = NULL, *cn = NULL;
+    char *ch = NULL, *ss = NULL, *sr = NULL;
+    char *t = NULL;
     char at[AVAHI_ADDRESS_STR_MAX];
     AvahiStringList *l;
     pa_module *m;
@@ -169,6 +172,51 @@ static void resolver_cb(
         if (pa_streq(key, "device")) {
             device = value;
             value = NULL;
+        } else if (pa_streq(key, "tp")) {
+            /* Transport protocol:
+             *  - TCP = only TCP,
+             *  - UDP = only UDP,
+             *  - TCP,UDP = both supported (UDP should be prefered) */
+            if (pa_str_in_list(value, ",", "UDP"))
+                tp = pa_xstrdup("UDP");
+            else if (pa_str_in_list(value, ",", "TCP"))
+                tp = pa_xstrdup("TCP");
+            else
+                tp = pa_xstrdup(value);
+        } else if (pa_streq(key, "et")) {
+            /* Supported encryption types:
+             *  - 0 = none,
+             *  - 1 = RSA,
+             *  - 2 = FairPlay,
+             *  - 3 = MFiSAP,
+             *  - 4 = FairPlay SAPv2.5. */
+             if (pa_str_in_list(value, ",", "1"))
+                 et = pa_xstrdup("RSA");
+             else
+                 et = pa_xstrdup("none");
+        } else if (pa_streq(key, "cn")) {
+            /* Suported audio codecs:
+             *  - 0 = PCM,
+             *  - 1 = ALAC,
+             *  - 2 = AAC,
+             *  - 3 = AAC ELD. */
+            cn = pa_xstrdup("PCM");
+        } else if (pa_streq(key, "md")) {
+            /* Supported metadata types:
+             *  - 0 = text,
+             *  - 1 = artwork,
+             *  - 2 = progress. */
+        } else if (pa_streq(key, "pw")) {
+            /* Requires password ? (true/false) */
+        } else if (pa_streq(key, "ch")) {
+            /* Number of channels */
+            ch = pa_xstrdup(value);
+        } else if (pa_streq(key, "ss")) {
+            /* Sample size */
+            ss = pa_xstrdup(value);
+        } else if (pa_streq(key, "sr")) {
+            /* Sample rate */
+            sr = pa_xstrdup(value);
         }
 
         avahi_free(key);
@@ -176,9 +224,9 @@ static void resolver_cb(
     }
 
     if (device)
-        dname = pa_sprintf_malloc("raop.%s.%s", host_name, device);
+        dname = pa_sprintf_malloc("raop_output.%s.%s", host_name, device);
     else
-        dname = pa_sprintf_malloc("raop.%s", host_name);
+        dname = pa_sprintf_malloc("raop_output.%s", host_name);
 
     if (!(vname = pa_namereg_make_valid_name(dname))) {
         pa_log("Cannot construct valid device name from '%s'.", dname);
@@ -205,6 +253,43 @@ static void resolver_cb(
                                  vname);
     }
 
+    if (tp != NULL) {
+        t = args;
+        args = pa_sprintf_malloc("%s protocol=%s", args, tp);
+        pa_xfree(tp);
+        pa_xfree(t);
+    }
+    if (et != NULL) {
+        t = args;
+        args = pa_sprintf_malloc("%s encryption=%s", args, et);
+        pa_xfree(et);
+        pa_xfree(t);
+    }
+    if (cn != NULL) {
+        t = args;
+        args = pa_sprintf_malloc("%s codec=%s", args, cn);
+        pa_xfree(cn);
+        pa_xfree(t);
+    }
+    if (ch != NULL) {
+        t = args;
+        args = pa_sprintf_malloc("%s channels=%s", args, ch);
+        pa_xfree(ch);
+        pa_xfree(t);
+    }
+    if (ss != NULL) {
+        t = args;
+        args = pa_sprintf_malloc("%s format=%s", args, ss);
+        pa_xfree(ss);
+        pa_xfree(t);
+    }
+    if (sr != NULL) {
+        t = args;
+        args = pa_sprintf_malloc("%s rate=%s", args, sr);
+        pa_xfree(sr);
+        pa_xfree(t);
+    }
+
     pa_log_debug("Loading module-raop-sink with arguments '%s'", args);
 
     if ((m = pa_module_load(u->core, "module-raop-sink", args))) {

commit 1e51282621aa9cec8cc4c946cf74f9f751b088bc
Author: Hajime Fujita <crisp.fujita at nifty.com>
Date:   Sun Nov 6 12:53:58 2016 -0600

    raop: Add UDP protocol handling
    
    There are two versions in the RAOP protocol; one uses TCP and the
    other uses UDP. Current raop implementation only supports TCP
    version.
    
    This patch adds an initial UDP protocol support for RAOP.
    It is based on Martin Blanchard's work
    (http://repo.or.cz/w/pulseaudio-raopUDP.git/shortlog/refs/heads/raop)
    which is inspired by Christophe Fergeau's work
    (https://github.com/zx2c4/pulseaudio-raop2).
    
    Matrin's modifications were edited by Hajime Fujita, so that it
    would support both TCP and UDP protocol in a single module.
    
    Also this patch includes a fix that was found thanks to Matthias,
    who reported that his ALAC
    codec support fixed the issue.
    https://bugs.freedesktop.org/show_bug.cgi?id=42804#c30

diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c
index c49ad52..ad19cad 100644
--- a/src/modules/raop/module-raop-sink.c
+++ b/src/modules/raop/module-raop-sink.c
@@ -66,12 +66,13 @@ PA_MODULE_USAGE(
         "sink_name=<name for the sink> "
         "sink_properties=<properties for the sink> "
         "server=<address>  "
+        "protocol=<transport protocol> "
+        "encryption=<encryption type> "
+        "codec=<audio codec> "
         "format=<sample format> "
         "rate=<sample rate> "
         "channels=<number of channels>");
 
-#define DEFAULT_SINK_NAME "raop"
-
 struct userdata {
     pa_core *core;
     pa_module *module;
@@ -82,6 +83,8 @@ struct userdata {
     pa_rtpoll_item *rtpoll_item;
     pa_thread *thread;
 
+    pa_raop_protocol_t protocol;
+
     pa_memchunk raw_memchunk;
     pa_memchunk encoded_memchunk;
 
@@ -97,7 +100,6 @@ struct userdata {
     int32_t rate;
 
     pa_smoother *smoother;
-    int fd;
 
     int64_t offset;
     int64_t encoding_overhead;
@@ -107,12 +109,26 @@ struct userdata {
     pa_raop_client *raop;
 
     size_t block_size;
+
+    /* Members only for the TCP protocol */
+    int tcp_fd;
+
+    /* Members only for the UDP protocol */
+    int udp_control_fd;
+    int udp_timing_fd;
+
+    /* For UDP thread wakeup clock calculation */
+    pa_usec_t udp_playback_start;
+    uint32_t  udp_sent_packets;
 };
 
 static const char* const valid_modargs[] = {
     "sink_name",
     "sink_properties",
     "server",
+    "protocol",
+    "encryption",
+    "codec",
     "format",
     "rate",
     "channels",
@@ -120,23 +136,26 @@ static const char* const valid_modargs[] = {
 };
 
 enum {
-    SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX,
-    SINK_MESSAGE_RIP_SOCKET
+    SINK_MESSAGE_TCP_PASS_SOCKET = PA_SINK_MESSAGE_MAX,
+    SINK_MESSAGE_TCP_RIP_SOCKET,
+    SINK_MESSAGE_UDP_SETUP,
+    SINK_MESSAGE_UDP_RECORD,
+    SINK_MESSAGE_UDP_DISCONNECTED,
 };
 
 /* Forward declarations: */
 static void sink_set_volume_cb(pa_sink *);
 
-static void on_connection(int fd, void *userdata) {
+static void tcp_on_connection(int fd, void *userdata) {
     int so_sndbuf = 0;
     socklen_t sl = sizeof(int);
     struct userdata *u = userdata;
     pa_assert(u);
 
-    pa_assert(u->fd < 0);
-    u->fd = fd;
+    pa_assert(u->tcp_fd < 0);
+    u->tcp_fd = fd;
 
-    if (getsockopt(u->fd, SOL_SOCKET, SO_SNDBUF, &so_sndbuf, &sl) < 0)
+    if (getsockopt(u->tcp_fd, SOL_SOCKET, SO_SNDBUF, &so_sndbuf, &sl) < 0)
         pa_log_warn("getsockopt(SO_SNDBUF) failed: %s", pa_cstrerror(errno));
     else {
         pa_log_debug("SO_SNDBUF is %zu.", (size_t) so_sndbuf);
@@ -148,19 +167,28 @@ static void on_connection(int fd, void *userdata) {
 
     pa_log_debug("Connection authenticated, handing fd to IO thread...");
 
-    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL);
+    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_TCP_PASS_SOCKET, NULL, 0, NULL, NULL);
 }
 
-static void on_close(void*userdata) {
+static void tcp_on_close(void*userdata) {
     struct userdata *u = userdata;
     pa_assert(u);
 
     pa_log_debug("Connection closed, informing IO thread...");
 
-    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RIP_SOCKET, NULL, 0, NULL, NULL);
+    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_TCP_RIP_SOCKET, NULL, 0, NULL, NULL);
+}
+
+static pa_usec_t sink_get_latency(const struct userdata *u) {
+    pa_usec_t w, r;
+
+    r = pa_smoother_get(u->smoother, pa_rtclock_now());
+    w = pa_bytes_to_usec((u->offset - u->encoding_overhead + (u->encoded_memchunk.length / u->encoding_ratio)), &u->sink->sample_spec);
+
+    return w > r ? w - r : 0;
 }
 
-static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+static int tcp_sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
     struct userdata *u = PA_SINK(o)->userdata;
 
     switch (code) {
@@ -175,8 +203,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
                     pa_smoother_pause(u->smoother, pa_rtclock_now());
 
                     /* Issue a FLUSH if we are connected. */
-                    if (u->fd >= 0) {
-                        pa_raop_flush(u->raop);
+                    if (u->tcp_fd >= 0) {
+                        pa_raop_client_flush(u->raop);
                     }
                     break;
 
@@ -188,10 +216,10 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
                         /* The connection can be closed when idle, so check to
                          * see if we need to reestablish it. */
-                        if (u->fd < 0)
-                            pa_raop_connect(u->raop);
+                        if (u->tcp_fd < 0)
+                            pa_raop_client_connect(u->raop);
                         else
-                            pa_raop_flush(u->raop);
+                            pa_raop_client_flush(u->raop);
                     }
 
                     break;
@@ -205,37 +233,32 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             break;
 
         case PA_SINK_MESSAGE_GET_LATENCY: {
-            pa_usec_t w, r;
-
-            r = pa_smoother_get(u->smoother, pa_rtclock_now());
-            w = pa_bytes_to_usec((u->offset - u->encoding_overhead + (u->encoded_memchunk.length / u->encoding_ratio)), &u->sink->sample_spec);
-
-            *((pa_usec_t*) data) = w > r ? w - r : 0;
+            *((pa_usec_t*) data) = sink_get_latency(u);
             return 0;
         }
 
-        case SINK_MESSAGE_PASS_SOCKET: {
+        case SINK_MESSAGE_TCP_PASS_SOCKET: {
             struct pollfd *pollfd;
 
             pa_assert(!u->rtpoll_item);
 
             u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
             pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
-            pollfd->fd = u->fd;
+            pollfd->fd = u->tcp_fd;
             pollfd->events = POLLOUT;
             /*pollfd->events = */pollfd->revents = 0;
 
             if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
                 /* Our stream has been suspended so we just flush it... */
-                pa_raop_flush(u->raop);
+                pa_raop_client_flush(u->raop);
             }
             return 0;
         }
 
-        case SINK_MESSAGE_RIP_SOCKET: {
-            if (u->fd >= 0) {
-                pa_close(u->fd);
-                u->fd = -1;
+        case SINK_MESSAGE_TCP_RIP_SOCKET: {
+            if (u->tcp_fd >= 0) {
+                pa_close(u->tcp_fd);
+                u->tcp_fd = -1;
             } else
                 /* FIXME */
                 pa_log("We should not get to this state. Cannot rip socket if not connected.");
@@ -260,10 +283,140 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
     return pa_sink_process_msg(o, code, data, offset, chunk);
 }
 
+static void udp_start_wakeup_clock(struct userdata *u) {
+    pa_usec_t now = pa_rtclock_now();
+
+    u->udp_playback_start = now;
+    u->udp_sent_packets = 0;
+    pa_rtpoll_set_timer_absolute(u->rtpoll, now);
+}
+
+static pa_usec_t udp_next_wakeup_clock(struct userdata *u) {
+    pa_usec_t intvl = pa_bytes_to_usec(u->block_size * u->udp_sent_packets,
+                                       &u->sink->sample_spec);
+    /* FIXME: how long until (u->block_size * u->udp_sent_packets) wraps?? */
+
+    return u->udp_playback_start + intvl;
+}
+
+static int udp_sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+        case PA_SINK_MESSAGE_SET_STATE:
+            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+                case PA_SINK_SUSPENDED:
+                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+                    pa_log_debug("RAOP: SUSPENDED");
+                    pa_smoother_pause(u->smoother, pa_rtclock_now());
+
+                    if (pa_raop_client_udp_can_stream(u->raop)) {
+                        /* Issue a TEARDOWN if we are still connected. */
+                        pa_raop_client_teardown(u->raop);
+                    }
+
+                    break;
+
+                case PA_SINK_IDLE:
+                    pa_log_debug("RAOP: IDLE");
+                    /* Issue a flush if we're comming from running state. */
+                    if (u->sink->thread_info.state == PA_SINK_RUNNING) {
+                        pa_rtpoll_set_timer_disabled(u->rtpoll);
+                        pa_raop_client_flush(u->raop);
+                    }
+
+                    break;
+
+                case PA_SINK_RUNNING:
+                    pa_log_debug("RAOP: RUNNING");
+
+                    pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
+
+                    if (!pa_raop_client_udp_can_stream(u->raop)) {
+                        /* Connecting will trigger a RECORD */
+                        pa_raop_client_connect(u->raop);
+                    }
+                    udp_start_wakeup_clock(u);
+
+                    break;
+
+                case PA_SINK_UNLINKED:
+                case PA_SINK_INIT:
+                case PA_SINK_INVALID_STATE:
+                    ;
+            }
+
+            break;
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t r = 0;
+
+            if (pa_raop_client_udp_can_stream(u->raop))
+                r = sink_get_latency(u);
+
+            *((pa_usec_t*) data) = r;
+
+            return 0;
+        }
+
+        case SINK_MESSAGE_UDP_SETUP: {
+            struct pollfd *pollfd;
+
+            u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 2);
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+            pollfd->fd = u->udp_control_fd;
+            pollfd->events = POLLIN | POLLPRI;
+            pollfd->revents = 0;
+            pollfd++;
+            pollfd->fd = u->udp_timing_fd;
+            pollfd->events = POLLIN | POLLPRI;
+            pollfd->revents = 0;
+
+            return 0;
+        }
+
+        case SINK_MESSAGE_UDP_RECORD: {
+            udp_start_wakeup_clock(u);
+
+            if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+                /* Our stream has been suspended so we just flush it... */
+                pa_rtpoll_set_timer_disabled(u->rtpoll);
+                pa_raop_client_flush(u->raop);
+            }
+
+            return 0;
+        }
+
+        case SINK_MESSAGE_UDP_DISCONNECTED: {
+            if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+                pa_rtpoll_set_timer_disabled(u->rtpoll);
+                if (u->rtpoll_item)
+                    pa_rtpoll_item_free(u->rtpoll_item);
+                u->rtpoll_item = NULL;
+            } else {
+                /* Question: is this valid here: or should we do some sort of:
+                 * return pa_sink_process_msg(PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL); ?? */
+                pa_module_unload_request(u->module, true);
+            }
+
+            pa_close(u->udp_control_fd);
+            pa_close(u->udp_timing_fd);
+
+            u->udp_control_fd = -1;
+            u->udp_timing_fd = -1;
+
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
 static void sink_set_volume_cb(pa_sink *s) {
     struct userdata *u = s->userdata;
     pa_cvolume hw;
-    pa_volume_t v;
+    pa_volume_t v, v_orig;
     char t[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
 
     pa_assert(u);
@@ -277,11 +430,16 @@ static void sink_set_volume_cb(pa_sink *s) {
      * any variation in channel volumes in software. */
     v = pa_cvolume_max(&s->real_volume);
 
+    v_orig = v;
+    v = pa_raop_client_adjust_volume(u->raop, v_orig);
+
+    pa_log_debug("Volume adjusted: orig=%u adjusted=%u", v_orig, v);
+
     /* Create a pa_cvolume version of our single value. */
     pa_cvolume_set(&hw, s->sample_spec.channels, v);
 
-    /* Perform any software manipulation of the volume needed. */
-    pa_sw_cvolume_divide(&s->soft_volume, &s->real_volume, &hw);
+    /* Set the real volume based on given original volume. */
+    pa_cvolume_set(&s->real_volume, s->sample_spec.channels, v_orig);
 
     pa_log_debug("Requested volume: %s", pa_cvolume_snprint_verbose(t, sizeof(t), &s->real_volume, &s->channel_map, false));
     pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint_verbose(t, sizeof(t), &hw, &s->channel_map, false));
@@ -305,8 +463,50 @@ static void sink_set_mute_cb(pa_sink *s) {
     }
 }
 
-static void thread_func(void *userdata) {
+static void udp_setup_cb(int control_fd, int timing_fd, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(control_fd);
+    pa_assert(timing_fd);
+    pa_assert(u);
+
+    u->udp_control_fd = control_fd;
+    u->udp_timing_fd = timing_fd;
+
+    pa_log_debug("Connection authenticated, syncing with server...");
+
+    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UDP_SETUP, NULL, 0, NULL, NULL);
+}
+
+static void udp_record_cb(void *userdata) {
     struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    /* Set the initial volume. */
+    sink_set_volume_cb(u->sink);
+
+    pa_log_debug("Synchronization done, pushing job to IO thread...");
+
+    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UDP_RECORD, NULL, 0, NULL, NULL);
+}
+
+static void udp_disconnected_cb(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    /* This callback function is called from both STATE_TEARDOWN and
+       STATE_DISCONNECTED in raop_client.c */
+
+    pa_assert(u);
+
+    pa_log_debug("Connection closed, informing IO thread...");
+
+    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UDP_DISCONNECTED, NULL, 0, NULL, NULL);
+}
+
+static void tcp_thread_func(struct userdata *u) {
     int write_type = 0;
     pa_memchunk silence;
     uint32_t silence_overhead = 0;
@@ -314,7 +514,7 @@ static void thread_func(void *userdata) {
 
     pa_assert(u);
 
-    pa_log_debug("Thread starting up");
+    pa_log_debug("TCP thread starting up");
 
     pa_thread_mq_install(&u->thread_mq);
 
@@ -394,7 +594,7 @@ static void thread_func(void *userdata) {
                     pa_assert(u->encoded_memchunk.length > 0);
 
                     p = pa_memblock_acquire(u->encoded_memchunk.memblock);
-                    l = pa_write(u->fd, (uint8_t*) p + u->encoded_memchunk.index, u->encoded_memchunk.length, &write_type);
+                    l = pa_write(u->tcp_fd, (uint8_t*) p + u->encoded_memchunk.index, u->encoded_memchunk.length, &write_type);
                     pa_memblock_release(u->encoded_memchunk.memblock);
 
                     pa_assert(l != 0);
@@ -443,7 +643,7 @@ static void thread_func(void *userdata) {
 #ifdef SIOCOUTQ
                 {
                     int l;
-                    if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
+                    if (ioctl(u->tcp_fd, SIOCOUTQ, &l) >= 0 && l > 0)
                         n -= (l / u->encoding_ratio);
                 }
 #endif
@@ -497,15 +697,139 @@ fail:
 finish:
     if (silence.memblock)
         pa_memblock_unref(silence.memblock);
-    pa_log_debug("Thread shutting down");
+    pa_log_debug("TCP thread shutting down");
+}
+
+static void udp_thread_func(struct userdata *u) {
+    pa_assert(u);
+
+    pa_log_debug("UDP thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
+
+    for (;;) {
+        pa_usec_t estimated;
+        int32_t overhead = 0;
+        ssize_t written = 0;
+        size_t length = 0;
+        int rv = 0;
+
+        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+            if (u->sink->thread_info.rewind_requested)
+                pa_sink_process_rewind(u->sink, 0);
+        }
+
+        /* Polling (audio data + control socket + timing socket). */
+        if ((rv = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+        else if (rv == 0)
+            goto finish;
+
+        if (!pa_rtpoll_timer_elapsed(u->rtpoll)) {
+            struct pollfd *pollfd;
+            uint8_t packet[32];
+            ssize_t read;
+
+            if (u->rtpoll_item) {
+                pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+                /* Event on the control socket ?? */
+                if (pollfd->revents & POLLIN) {
+                    pollfd->revents = 0;
+                    pa_log_debug("Received control packet.");
+                    read = pa_read(pollfd->fd, packet, sizeof(packet), NULL);
+                    pa_raop_client_udp_handle_control_packet(u->raop, packet, read);
+                }
+
+                pollfd++;
+
+                /* Event on the timing port ?? */
+                if (pollfd->revents & POLLIN) {
+                    pollfd->revents = 0;
+                    pa_log_debug("Received timing packet.");
+                    read = pa_read(pollfd->fd, packet, sizeof(packet), NULL);
+                    pa_raop_client_udp_handle_timing_packet(u->raop, packet, read);
+                }
+            }
+
+            continue;
+        }
+
+        if (!pa_raop_client_udp_can_stream(u->raop))
+            continue;
+        if (u->sink->thread_info.state != PA_SINK_RUNNING)
+            continue;
+
+        if (u->encoded_memchunk.length <= 0) {
+            if (u->encoded_memchunk.memblock != NULL)
+                pa_memblock_unref(u->encoded_memchunk.memblock);
+
+            if (u->raw_memchunk.length <= 0) {
+                if (u->raw_memchunk.memblock)
+                    pa_memblock_unref(u->raw_memchunk.memblock);
+                pa_memchunk_reset(&u->raw_memchunk);
+
+                /* Grab unencoded audio data from PulseAudio. */
+                pa_sink_render_full(u->sink, u->block_size, &u->raw_memchunk);
+            }
+
+            pa_assert(u->raw_memchunk.length > 0);
+
+            length = u->raw_memchunk.length;
+            pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk);
+            u->encoding_ratio = (double) u->encoded_memchunk.length / (double) (length - u->raw_memchunk.length);
+            overhead = u->encoded_memchunk.length - (length - u->raw_memchunk.length);
+        }
+
+        pa_assert(u->encoded_memchunk.length > 0);
+
+        written = pa_raop_client_udp_send_audio_packet(u->raop,&u->encoded_memchunk);
+        if (written < 0) {
+            pa_log("Failed to send UDP packet: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+
+        u->udp_sent_packets++;
+        /* Sleep until next packet transmission */
+        pa_rtpoll_set_timer_absolute(u->rtpoll, udp_next_wakeup_clock(u));
+
+        u->offset += written;
+        u->encoding_overhead += overhead;
+
+        estimated = pa_bytes_to_usec(u->offset - u->encoding_overhead, &u->sink->sample_spec);
+        pa_smoother_put(u->smoother, pa_rtclock_now(), estimated);
+    }
+
+fail:
+    /* If this was no regular exit, continue processing messages until PA_MESSAGE_SHUTDOWN. */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("UDP thread shutting down");
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    if (u->protocol == RAOP_TCP)
+        tcp_thread_func(u);
+    else if (u->protocol == RAOP_UDP)
+        udp_thread_func(u);
+    else
+        pa_assert(false);
+
+    return;
 }
 
 int pa__init(pa_module *m) {
     struct userdata *u = NULL;
     pa_sample_spec ss;
     pa_modargs *ma = NULL;
-    const char *server;
+    const char *server, *protocol, *encryption;
     pa_sink_new_data data;
+    char *t = NULL;
 
     pa_assert(m);
 
@@ -532,7 +856,7 @@ int pa__init(pa_module *m) {
     u->core = m->core;
     u->module = m;
     m->userdata = u;
-    u->fd = -1;
+    u->tcp_fd = -1;
     u->smoother = pa_smoother_new(
             PA_USEC_PER_SEC,
             PA_USEC_PER_SEC*2,
@@ -574,15 +898,32 @@ int pa__init(pa_module *m) {
         goto fail;
     }
 
+    /* This may be overwriten if sink_name is specified in module arguments. */
+    t = pa_sprintf_malloc("raop_client.%s", server);
+
+    protocol = pa_modargs_get_value(ma, "protocol", NULL);
+    if (protocol == NULL || pa_streq(protocol, "TCP")) {
+        /* Assume TCP by default */
+        u->protocol = RAOP_TCP;
+    }
+    else if (pa_streq(protocol, "UDP")) {
+        u->protocol = RAOP_UDP;
+    } else {
+        pa_log("Unsupported protocol argument given: %s", protocol);
+        goto fail;
+    }
+
     pa_sink_new_data_init(&data);
     data.driver = __FILE__;
     data.module = m;
-    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", t));
     pa_sink_new_data_set_sample_spec(&data, &ss);
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server);
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "music");
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "RAOP sink '%s'", server);
 
+    /* RAOP discover module will eventually overwrite sink_name and others
+       (PA_UPDATE_REPLACE). */
     if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
         pa_log("Invalid properties");
         pa_sink_new_data_done(&data);
@@ -590,6 +931,7 @@ int pa__init(pa_module *m) {
     }
 
     u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_NETWORK);
+    pa_xfree(t); t = NULL;
     pa_sink_new_data_done(&data);
 
     if (!u->sink) {
@@ -597,7 +939,10 @@ int pa__init(pa_module *m) {
         goto fail;
     }
 
-    u->sink->parent.process_msg = sink_process_msg;
+    if (u->protocol == RAOP_TCP)
+        u->sink->parent.process_msg = tcp_sink_process_msg;
+    else
+        u->sink->parent.process_msg = udp_sink_process_msg;
     u->sink->userdata = u;
     pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
     pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
@@ -606,13 +951,27 @@ int pa__init(pa_module *m) {
     pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
     pa_sink_set_rtpoll(u->sink, u->rtpoll);
 
-    if (!(u->raop = pa_raop_client_new(u->core, server))) {
+    if (!(u->raop = pa_raop_client_new(u->core, server, u->protocol))) {
         pa_log("Failed to connect to server.");
         goto fail;
     }
 
-    pa_raop_client_set_callback(u->raop, on_connection, u);
-    pa_raop_client_set_closed_callback(u->raop, on_close, u);
+    encryption = pa_modargs_get_value(ma, "encryption", NULL);
+    pa_raop_client_set_encryption(u->raop, !pa_safe_streq(encryption, "none"));
+
+    pa_raop_client_tcp_set_callback(u->raop, tcp_on_connection, u);
+    pa_raop_client_tcp_set_closed_callback(u->raop, tcp_on_close, u);
+
+    if (u->protocol == RAOP_UDP) {
+        /* The number of frames per blocks is not negotiable... */
+        pa_raop_client_udp_get_blocks_size(u->raop, &u->block_size);
+        u->block_size *= pa_frame_size(&ss);
+        pa_sink_set_max_request(u->sink, u->block_size);
+
+        pa_raop_client_udp_set_setup_callback(u->raop, udp_setup_cb, u);
+        pa_raop_client_udp_set_record_callback(u->raop, udp_record_cb, u);
+        pa_raop_client_udp_set_disconnected_callback(u->raop, udp_disconnected_cb, u);
+    }
 
     if (!(u->thread = pa_thread_new("raop-sink", thread_func, u))) {
         pa_log("Failed to create thread.");
@@ -626,6 +985,8 @@ int pa__init(pa_module *m) {
     return 0;
 
 fail:
+    pa_xfree(t);
+
     if (ma)
         pa_modargs_free(ma);
 
@@ -684,8 +1045,8 @@ void pa__done(pa_module *m) {
     if (u->smoother)
         pa_smoother_free(u->smoother);
 
-    if (u->fd >= 0)
-        pa_close(u->fd);
+    if (u->tcp_fd >= 0)
+        pa_close(u->tcp_fd);
 
     pa_xfree(u);
 }
diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
index 31e0fdc..4e8f474 100644
--- a/src/modules/raop/raop_client.c
+++ b/src/modules/raop/raop_client.c
@@ -26,6 +26,7 @@
 #include <errno.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
+#include <math.h>
 
 #ifdef HAVE_SYS_FILIO_H
 #include <sys/filio.h>
@@ -39,10 +40,14 @@
 #include <openssl/engine.h>
 
 #include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/sample.h>
 
 #include <pulsecore/core-error.h>
+#include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/iochannel.h>
+#include <pulsecore/arpa-inet.h>
 #include <pulsecore/socket-util.h>
 #include <pulsecore/log.h>
 #include <pulsecore/parseaddr.h>
@@ -54,6 +59,7 @@
 #include "rtsp_client.h"
 #include "base64.h"
 
+#define UDP_FRAMES_PER_PACKET 352
 #define AES_CHUNKSIZE 16
 
 #define JACK_STATUS_DISCONNECTED 0
@@ -66,7 +72,19 @@
 #define VOLUME_MIN -144
 #define VOLUME_MAX 0
 
-#define RAOP_PORT 5000
+#define DEFAULT_RAOP_PORT 5000
+#define UDP_DEFAULT_AUDIO_PORT 6000
+#define UDP_DEFAULT_CONTROL_PORT 6001
+#define UDP_DEFAULT_TIMING_PORT 6002
+
+typedef enum {
+    UDP_PAYLOAD_TIMING_REQUEST = 0x52,
+    UDP_PAYLOAD_TIMING_RESPONSE = 0x53,
+    UDP_PAYLOAD_SYNCHRONIZATION = 0x54,
+    UDP_PAYLOAD_RETRANSMIT_REQUEST = 0x55,
+    UDP_PAYLOAD_RETRANSMIT_REPLY = 0x56,
+    UDP_PAYLOAD_AUDIO_DATA = 0x60
+} pa_raop_udp_payload_type;
 
 /* Openssl 1.1.0 broke compatibility. Before 1.1.0 we had to set RSA->n and
  * RSA->e manually, but after 1.1.0 the RSA struct is opaque and we have to use
@@ -89,26 +107,93 @@ struct pa_raop_client {
     uint16_t port;
     char *sid;
     pa_rtsp_client *rtsp;
+    pa_raop_protocol_t protocol;
 
     uint8_t jack_type;
     uint8_t jack_status;
 
     /* Encryption Related bits */
+    int encryption; /* Enable encryption? */
     AES_KEY aes;
     uint8_t aes_iv[AES_CHUNKSIZE]; /* Initialization vector for aes-cbc */
     uint8_t aes_nv[AES_CHUNKSIZE]; /* Next vector for aes-cbc */
     uint8_t aes_key[AES_CHUNKSIZE]; /* Key for aes-cbc */
 
-    pa_socket_client *sc;
-    int fd;
-
     uint16_t seq;
     uint32_t rtptime;
 
-    pa_raop_client_cb_t callback;
-    void *userdata;
-    pa_raop_client_closed_cb_t closed_callback;
-    void *closed_userdata;
+    /* Members only for the TCP protocol */
+    pa_socket_client *tcp_sc;
+    int tcp_fd;
+
+    pa_raop_client_cb_t tcp_callback;
+    void *tcp_userdata;
+    pa_raop_client_closed_cb_t tcp_closed_callback;
+    void *tcp_closed_userdata;
+
+    /* Members only for the UDP protocol */
+    uint16_t udp_my_control_port;
+    uint16_t udp_my_timing_port;
+    uint16_t udp_server_control_port;
+    uint16_t udp_server_timing_port;
+
+    int udp_stream_fd;
+    int udp_control_fd;
+    int udp_timing_fd;
+
+    uint32_t udp_ssrc;
+
+    bool udp_first_packet;
+    uint32_t udp_sync_interval;
+    uint32_t udp_sync_count;
+
+    pa_raop_client_setup_cb_t udp_setup_callback;
+    void *udp_setup_userdata;
+
+    pa_raop_client_record_cb_t udp_record_callback;
+    void *udp_record_userdata;
+
+    pa_raop_client_disconnected_cb_t udp_disconnected_callback;
+    void *udp_disconnected_userdata;
+};
+
+/* Timming packet header (8x8):
+ *  [0]   RTP v2: 0x80,
+ *  [1]   Payload type: 0x53 | marker bit: 0x80,
+ *  [2,3] Sequence number: 0x0007,
+ *  [4,7] Timestamp: 0x00000000 (unused). */
+static const uint8_t udp_timming_header[8] = {
+    0x80, 0xd3, 0x00, 0x07,
+    0x00, 0x00, 0x00, 0x00
+};
+
+/* Sync packet header (8x8):
+ *  [0]   RTP v2: 0x80,
+ *  [1]   Payload type: 0x54 | marker bit: 0x80,
+ *  [2,3] Sequence number: 0x0007,
+ *  [4,7] Timestamp: 0x00000000 (to be set). */
+static const uint8_t udp_sync_header[8] = {
+    0x80, 0xd4, 0x00, 0x07,
+    0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t tcp_audio_header[16] = {
+    0x24, 0x00, 0x00, 0x00,
+    0xF0, 0xFF, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00,
+};
+
+/* Audio packet header (12x8):
+ *  [0]    RTP v2: 0x80,
+ *  [1]    Payload type: 0x60,
+ *  [2,3]  Sequence number: 0x0000 (to be set),
+ *  [4,7]  Timestamp: 0x00000000 (to be set),
+ *  [8,12] SSRC: 0x00000000 (to be set).*/
+static const uint8_t udp_audio_header[12] = {
+    0x80, 0x60, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00
 };
 
 /**
@@ -218,84 +303,334 @@ static inline void rtrimchar(char *str, char rc) {
     }
 }
 
-static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
+static void tcp_on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
     pa_raop_client *c = userdata;
 
     pa_assert(sc);
     pa_assert(c);
-    pa_assert(c->sc == sc);
-    pa_assert(c->fd < 0);
-    pa_assert(c->callback);
+    pa_assert(c->tcp_sc == sc);
+    pa_assert(c->tcp_fd < 0);
+    pa_assert(c->tcp_callback);
 
-    pa_socket_client_unref(c->sc);
-    c->sc = NULL;
+    pa_socket_client_unref(c->tcp_sc);
+    c->tcp_sc = NULL;
 
     if (!io) {
         pa_log("Connection failed: %s", pa_cstrerror(errno));
         return;
     }
 
-    c->fd = pa_iochannel_get_send_fd(io);
+    c->tcp_fd = pa_iochannel_get_send_fd(io);
 
     pa_iochannel_set_noclose(io, true);
     pa_iochannel_free(io);
 
-    pa_make_tcp_socket_low_delay(c->fd);
+    pa_make_tcp_socket_low_delay(c->tcp_fd);
 
     pa_log_debug("Connection established");
-    c->callback(c->fd, c->userdata);
+    c->tcp_callback(c->tcp_fd, c->tcp_userdata);
 }
 
-static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist *headers, void *userdata) {
-    pa_raop_client *c = userdata;
+static inline uint64_t timeval_to_ntp(struct timeval *tv) {
+    uint64_t ntp = 0;
+
+    /* Converting micro seconds to a fraction. */
+    ntp = (uint64_t) tv->tv_usec * UINT32_MAX / PA_USEC_PER_SEC;
+    /* Moving reference from  1 Jan 1970 to 1 Jan 1900 (seconds). */
+    ntp |= (uint64_t) (tv->tv_sec + 0x83aa7e80) << 32;
+
+    return ntp;
+}
+
+static int connect_udp_socket(pa_raop_client *c, int fd, uint16_t port) {
+    struct sockaddr_in sa4;
+#ifdef HAVE_IPV6
+    struct sockaddr_in6 sa6;
+#endif
+    struct sockaddr *sa;
+    socklen_t salen;
+    sa_family_t af;
+
+    pa_zero(sa4);
+#ifdef HAVE_IPV6
+    pa_zero(sa6);
+#endif
+    if (inet_pton(AF_INET, c->host, &sa4.sin_addr) > 0) {
+        sa4.sin_family = af = AF_INET;
+        sa4.sin_port = htons(port);
+        sa = (struct sockaddr *) &sa4;
+        salen = sizeof(sa4);
+#ifdef HAVE_IPV6
+    } else if (inet_pton(AF_INET6, c->host, &sa6.sin6_addr) > 0) {
+        sa6.sin6_family = af = AF_INET6;
+        sa6.sin6_port = htons(port);
+        sa = (struct sockaddr *) &sa6;
+        salen = sizeof(sa6);
+#endif
+    } else {
+        pa_log("Invalid destination '%s'", c->host);
+        goto fail;
+    }
+
+    if (fd < 0 && (fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) {
+        pa_log("socket() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    /* If the socket queue is full, let's drop packets */
+    pa_make_udp_socket_low_delay(fd);
+    pa_make_fd_nonblock(fd);
+
+    if (connect(fd, sa, salen) < 0) {
+        pa_log("connect() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_log_debug("Connected to %s on port %d (SOCK_DGRAM)", c->host, port);
+    return fd;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return -1;
+}
+
+static int open_bind_udp_socket(pa_raop_client *c, uint16_t *actual_port) {
+    int fd = -1;
+    uint16_t port;
+    struct sockaddr_in sa4;
+#ifdef HAVE_IPV6
+    struct sockaddr_in6 sa6;
+#endif
+    struct sockaddr *sa;
+    uint16_t *sa_port;
+    socklen_t salen;
+    sa_family_t af;
+    int one = 1;
+
+    pa_assert(actual_port);
+
+    port = *actual_port;
+
+    pa_zero(sa4);
+#ifdef HAVE_IPV6
+    pa_zero(sa6);
+#endif
+    if (inet_pton(AF_INET, pa_rtsp_localip(c->rtsp), &sa4.sin_addr) > 0) {
+        sa4.sin_family = af = AF_INET;
+        sa4.sin_port = htons(port);
+        sa = (struct sockaddr *) &sa4;
+        salen = sizeof(sa4);
+        sa_port = &sa4.sin_port;
+#ifdef HAVE_IPV6
+    } else if (inet_pton(AF_INET6, pa_rtsp_localip(c->rtsp), &sa6.sin6_addr) > 0) {
+        sa6.sin6_family = af = AF_INET6;
+        sa6.sin6_port = htons(port);
+        sa = (struct sockaddr *) &sa6;
+        salen = sizeof(sa6);
+        sa_port = &sa6.sin6_port;
+#endif
+    } else {
+        pa_log("Could not determine which address family to use");
+        goto fail;
+    }
+
+    pa_zero(sa4);
+#ifdef HAVE_IPV6
+    pa_zero(sa6);
+#endif
+
+    if ((fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) {
+        pa_log("socket() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+#ifdef SO_TIMESTAMP
+    if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0) {
+        pa_log("setsockopt(SO_TIMESTAMP) failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+#else
+    pa_log("SO_TIMESTAMP unsupported on this platform");
+    goto fail;
+#endif
+
+    one = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
+        pa_log("setsockopt(SO_REUSEADDR) failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    do {
+        *sa_port = htons(port);
+
+        if (bind(fd, sa, salen) < 0 && errno != EADDRINUSE) {
+            pa_log("bind_socket() failed: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+        break;
+    } while (++port > 0);
+
+    pa_log_debug("Socket bound to port %d (SOCK_DGRAM)", port);
+    *actual_port = port;
+
+    return fd;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return -1;
+}
+
+static int udp_send_timing_packet(pa_raop_client *c, const uint32_t data[6], uint64_t received) {
+    uint32_t packet[8];
+    struct timeval tv;
+    ssize_t written = 0;
+    uint64_t trs = 0;
+    int rv = 1;
+
+    memcpy(packet, udp_timming_header, sizeof(udp_timming_header));
+    /* Copying originate timestamp from the incoming request packet. */
+    packet[2] = data[4];
+    packet[3] = data[5];
+    /* Set the receive timestamp to reception time. */
+    packet[4] = htonl(received >> 32);
+    packet[5] = htonl(received & 0xffffffff);
+    /* Set the transmit timestamp to current time. */
+    trs = timeval_to_ntp(pa_rtclock_get(&tv));
+    packet[6] = htonl(trs >> 32);
+    packet[7] = htonl(trs & 0xffffffff);
+
+    written = pa_loop_write(c->udp_timing_fd, packet, sizeof(packet), NULL);
+    if (written == sizeof(packet))
+        rv = 0;
+
+    return rv;
+}
+
+static int udp_send_sync_packet(pa_raop_client *c, uint32_t stamp) {
+    const uint32_t delay = 88200;
+    uint32_t packet[5];
+    struct timeval tv;
+    ssize_t written = 0;
+    uint64_t trs = 0;
+    int rv = 1;
+
+    memcpy(packet, udp_sync_header, sizeof(udp_sync_header));
+    if (c->udp_first_packet)
+        packet[0] |= 0x10;
+    stamp -= delay;
+    packet[1] = htonl(stamp);
+    /* Set the transmited timestamp to current time. */
+    trs = timeval_to_ntp(pa_rtclock_get(&tv));
+    packet[2] = htonl(trs >> 32);
+    packet[3] = htonl(trs & 0xffffffff);
+    stamp += delay;
+    packet[4] = htonl(stamp);
+
+    written = pa_loop_write(c->udp_control_fd, packet, sizeof(packet), NULL);
+    if (written == sizeof(packet))
+        rv = 0;
+
+    return rv;
+}
+
+static void udp_build_audio_header(pa_raop_client *c, uint32_t *buffer, size_t size) {
+    pa_assert(size >= sizeof(udp_audio_header));
+
+    memcpy(buffer, udp_audio_header, sizeof(udp_audio_header));
+    if (c->udp_first_packet)
+        buffer[0] |= htonl((uint32_t) 0x80 << 16);
+    buffer[0] |= htonl((uint32_t) c->seq);
+    buffer[1] = htonl(c->rtptime);
+    buffer[2] = htonl(c->udp_ssrc);
+}
+
+static ssize_t udp_send_audio_packet(pa_raop_client *c, uint8_t *buffer, size_t size) {
+    ssize_t length;
+
+    length = pa_write(c->udp_stream_fd, buffer, size, NULL);
+    c->seq++;
+
+    return length;
+}
+
+static void do_rtsp_announce(pa_raop_client *c) {
+    int i;
+    uint8_t rsakey[512];
+    char *key, *iv, *sac = NULL, *sdp;
+    uint16_t rand_data;
+    const char *ip;
+    char *url;
+
+    ip = pa_rtsp_localip(c->rtsp);
+    /* First of all set the url properly. */
+    url = pa_sprintf_malloc("rtsp://%s/%s", ip, c->sid);
+    pa_rtsp_set_url(c->rtsp, url);
+    pa_xfree(url);
+
+    /* Now encrypt our aes_public key to send to the device. */
+    i = rsa_encrypt(c->aes_key, AES_CHUNKSIZE, rsakey);
+    pa_base64_encode(rsakey, i, &key);
+    rtrimchar(key, '=');
+    pa_base64_encode(c->aes_iv, AES_CHUNKSIZE, &iv);
+    rtrimchar(iv, '=');
+
+    /* UDP protocol does not need "Apple-Challenge" at announce. */
+    if (c->protocol == RAOP_TCP) {
+        pa_random(&rand_data, sizeof(rand_data));
+        pa_base64_encode(&rand_data, AES_CHUNKSIZE, &sac);
+        rtrimchar(sac, '=');
+        pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
+    }
+
+    if (c->encryption)
+        sdp = pa_sprintf_malloc(
+            "v=0\r\n"
+            "o=iTunes %s 0 IN IP4 %s\r\n"
+            "s=iTunes\r\n"
+            "c=IN IP4 %s\r\n"
+            "t=0 0\r\n"
+            "m=audio 0 RTP/AVP 96\r\n"
+            "a=rtpmap:96 AppleLossless\r\n"
+            "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 44100\r\n"
+            "a=rsaaeskey:%s\r\n"
+            "a=aesiv:%s\r\n",
+            c->sid, ip, c->host,
+            c->protocol == RAOP_TCP ? 4096 : UDP_FRAMES_PER_PACKET,
+            key, iv);
+    else
+        sdp = pa_sprintf_malloc(
+            "v=0\r\n"
+            "o=iTunes %s 0 IN IP4 %s\r\n"
+            "s=iTunes\r\n"
+            "c=IN IP4 %s\r\n"
+            "t=0 0\r\n"
+            "m=audio 0 RTP/AVP 96\r\n"
+            "a=rtpmap:96 AppleLossless\r\n"
+            "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 44100\r\n",
+            c->sid, ip, c->host,
+            c->protocol == RAOP_TCP ? 4096 : UDP_FRAMES_PER_PACKET);
+
+    pa_rtsp_announce(c->rtsp, sdp);
+    pa_xfree(key);
+    pa_xfree(iv);
+    pa_xfree(sac);
+    pa_xfree(sdp);
+}
+
+static void tcp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *userdata) {
+    pa_raop_client* c = userdata;
     pa_assert(c);
     pa_assert(rtsp);
     pa_assert(rtsp == c->rtsp);
 
     switch (state) {
         case STATE_CONNECT: {
-            int i;
-            uint8_t rsakey[512];
-            char *key, *iv, *sac, *sdp;
-            uint16_t rand_data;
-            const char *ip;
-            char *url;
-
             pa_log_debug("RAOP: CONNECTED");
-            ip = pa_rtsp_localip(c->rtsp);
-            /* First of all set the url properly. */
-            url = pa_sprintf_malloc("rtsp://%s/%s", ip, c->sid);
-            pa_rtsp_set_url(c->rtsp, url);
-            pa_xfree(url);
-
-            /* Now encrypt our aes_public key to send to the device. */
-            i = rsa_encrypt(c->aes_key, AES_CHUNKSIZE, rsakey);
-            pa_base64_encode(rsakey, i, &key);
-            rtrimchar(key, '=');
-            pa_base64_encode(c->aes_iv, AES_CHUNKSIZE, &iv);
-            rtrimchar(iv, '=');
-
-            pa_random(&rand_data, sizeof(rand_data));
-            pa_base64_encode(&rand_data, AES_CHUNKSIZE, &sac);
-            rtrimchar(sac, '=');
-            pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
-            sdp = pa_sprintf_malloc(
-                "v=0\r\n"
-                "o=iTunes %s 0 IN IP4 %s\r\n"
-                "s=iTunes\r\n"
-                "c=IN IP4 %s\r\n"
-                "t=0 0\r\n"
-                "m=audio 0 RTP/AVP 96\r\n"
-                "a=rtpmap:96 AppleLossless\r\n"
-                "a=fmtp:96 4096 0 16 40 10 14 2 255 0 0 44100\r\n"
-                "a=rsaaeskey:%s\r\n"
-                "a=aesiv:%s\r\n",
-                c->sid, ip, c->host, key, iv);
-            pa_rtsp_announce(c->rtsp, sdp);
-            pa_xfree(key);
-            pa_xfree(iv);
-            pa_xfree(sac);
-            pa_xfree(sdp);
+            do_rtsp_announce(c);
             break;
         }
 
@@ -343,11 +678,11 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist *he
             uint32_t port = pa_rtsp_serverport(c->rtsp);
             pa_log_debug("RAOP: RECORDED");
 
-            if (!(c->sc = pa_socket_client_new_string(c->core->mainloop, true, c->host, port))) {
+            if (!(c->tcp_sc = pa_socket_client_new_string(c->core->mainloop, true, c->host, port))) {
                 pa_log("failed to connect to server '%s:%d'", c->host, port);
                 return;
             }
-            pa_socket_client_set_callback(c->sc, on_connection, c);
+            pa_socket_client_set_callback(c->tcp_sc, tcp_on_connection, c);
             break;
         }
 
@@ -364,30 +699,328 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist *he
             break;
 
         case STATE_DISCONNECTED:
-            pa_assert(c->closed_callback);
+            pa_assert(c->tcp_closed_callback);
             pa_assert(c->rtsp);
 
             pa_log_debug("RTSP control channel closed");
             pa_rtsp_client_free(c->rtsp);
             c->rtsp = NULL;
-            if (c->fd > 0) {
-                /* We do not close the fd, we leave it to the closed callback to do that. */
-                c->fd = -1;
+            if (c->tcp_fd > 0) {
+                /* We do not close the fd, we leave it to the closed callback to do that */
+                c->tcp_fd = -1;
             }
-            if (c->sc) {
-                pa_socket_client_unref(c->sc);
-                c->sc = NULL;
+            if (c->tcp_sc) {
+                pa_socket_client_unref(c->tcp_sc);
+                c->tcp_sc = NULL;
             }
             pa_xfree(c->sid);
             c->sid = NULL;
-            c->closed_callback(c->closed_userdata);
+            c->tcp_closed_callback(c->tcp_closed_userdata);
             break;
     }
 }
 
-pa_raop_client* pa_raop_client_new(pa_core *core, const char *host) {
-    pa_parsed_address a;
+static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist *headers, void *userdata) {
+    pa_raop_client *c = userdata;
+
+    pa_assert(c);
+    pa_assert(rtsp);
+    pa_assert(rtsp == c->rtsp);
+
+    switch (state) {
+        case STATE_CONNECT: {
+            uint16_t rand;
+            char *sac;
+
+            /* Set the Apple-Challenge key */
+            pa_random(&rand, sizeof(rand));
+            pa_base64_encode(&rand, AES_CHUNKSIZE, &sac);
+            rtrimchar(sac, '=');
+            pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
+
+            pa_rtsp_options(c->rtsp);
+
+            pa_xfree(sac);
+            break;
+        }
+
+        case STATE_OPTIONS: {
+            pa_log_debug("RAOP: OPTIONS");
+
+            pa_rtsp_remove_header(c->rtsp, "Apple-Challenge");
+            do_rtsp_announce(c);
+            break;
+        }
+
+        case STATE_ANNOUNCE: {
+            char *trs;
+
+            pa_assert(c->udp_control_fd < 0);
+            pa_assert(c->udp_timing_fd < 0);
+
+            c->udp_control_fd = open_bind_udp_socket(c, &c->udp_my_control_port);
+            if (c->udp_control_fd < 0)
+                goto error_announce;
+            c->udp_timing_fd  = open_bind_udp_socket(c, &c->udp_my_timing_port);
+            if (c->udp_timing_fd < 0)
+                goto error_announce;
+
+            trs = pa_sprintf_malloc("RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=%d;timing_port=%d",
+                c->udp_my_control_port,
+                c->udp_my_timing_port);
+
+            pa_rtsp_setup(c->rtsp, trs);
+
+            pa_xfree(trs);
+            break;
+
+        error_announce:
+            if (c->udp_control_fd > 0) {
+                pa_close(c->udp_control_fd);
+                c->udp_control_fd = -1;
+            }
+            if (c->udp_timing_fd > 0) {
+                pa_close(c->udp_timing_fd);
+                c->udp_timing_fd = -1;
+            }
+
+            pa_rtsp_client_free(c->rtsp);
+            c->rtsp = NULL;
+
+            c->udp_my_control_port     = UDP_DEFAULT_CONTROL_PORT;
+            c->udp_server_control_port = UDP_DEFAULT_CONTROL_PORT;
+            c->udp_my_timing_port      = UDP_DEFAULT_TIMING_PORT;
+            c->udp_server_timing_port  = UDP_DEFAULT_TIMING_PORT;
+
+            pa_log_error("aborting RTSP announce, failed creating required sockets");
+        }
+
+        case STATE_SETUP: {
+            uint32_t stream_port = UDP_DEFAULT_AUDIO_PORT;
+            char *ajs, *trs, *token, *pc;
+            char delimiters[] = ";";
+            const char *token_state = NULL;
+            uint32_t port = 0;
+            int ret;
+
+            pa_log_debug("RAOP: SETUP");
+
+            ajs = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Jack-Status"));
+            trs = pa_xstrdup(pa_headerlist_gets(headers, "Transport"));
+
+            if (ajs) {
+                c->jack_type = JACK_TYPE_ANALOG;
+                c->jack_status = JACK_STATUS_DISCONNECTED;
+
+                while ((token = pa_split(ajs, delimiters, &token_state))) {
+                    if ((pc = strstr(token, "="))) {
+                      *pc = 0;
+                      if (pa_streq(token, "type") && pa_streq(pc + 1, "digital"))
+                          c->jack_type = JACK_TYPE_DIGITAL;
+                    } else {
+                        if (pa_streq(token, "connected"))
+                            c->jack_status = JACK_STATUS_CONNECTED;
+                    }
+                    pa_xfree(token);
+                }
+
+            } else {
+                pa_log_warn("Audio-Jack-Status missing");
+            }
+
+            token_state = NULL;
+
+            if (trs) {
+                /* Now parse out the server port component of the response. */
+                while ((token = pa_split(trs, delimiters, &token_state))) {
+                    if ((pc = strstr(token, "="))) {
+                        *pc = 0;
+                        if (pa_streq(token, "control_port")) {
+                            port = 0;
+                            pa_atou(pc + 1, &port);
+                            c->udp_server_control_port = port;
+                        }
+                        if (pa_streq(token, "timing_port")) {
+                            port = 0;
+                            pa_atou(pc + 1, &port);
+                            c->udp_server_timing_port = port;
+                        }
+                        *pc = '=';
+                    }
+                    pa_xfree(token);
+                }
+            } else {
+                pa_log_warn("Transport missing");
+            }
+
+            pa_xfree(ajs);
+            pa_xfree(trs);
+
+            stream_port = pa_rtsp_serverport(c->rtsp);
+            if (stream_port == 0)
+                goto error;
+            if (c->udp_server_control_port == 0 || c->udp_server_timing_port == 0)
+                goto error;
+
+            pa_log_debug("Using server_port=%d, control_port=%d & timing_port=%d",
+                stream_port,
+                c->udp_server_control_port,
+                c->udp_server_timing_port);
+
+            pa_assert(c->udp_stream_fd < 0);
+            pa_assert(c->udp_control_fd >= 0);
+            pa_assert(c->udp_timing_fd >= 0);
+
+            c->udp_stream_fd = connect_udp_socket(c, -1, stream_port);
+            if (c->udp_stream_fd <= 0)
+                goto error;
+            ret = connect_udp_socket(c, c->udp_control_fd,
+                                     c->udp_server_control_port);
+            if (ret < 0)
+                goto error;
+            ret = connect_udp_socket(c, c->udp_timing_fd,
+                                     c->udp_server_timing_port);
+            if (ret < 0)
+                goto error;
+
+            c->udp_setup_callback(c->udp_control_fd, c->udp_timing_fd, c->udp_setup_userdata);
+            pa_rtsp_record(c->rtsp, &c->seq, &c->rtptime);
+
+            break;
+
+        error:
+            if (c->udp_stream_fd > 0) {
+                pa_close(c->udp_stream_fd);
+                c->udp_stream_fd = -1;
+            }
+            if (c->udp_control_fd > 0) {
+                pa_close(c->udp_control_fd);
+                c->udp_control_fd = -1;
+            }
+            if (c->udp_timing_fd > 0) {
+                pa_close(c->udp_timing_fd);
+                c->udp_timing_fd = -1;
+            }
+
+            pa_rtsp_client_free(c->rtsp);
+            c->rtsp = NULL;
+
+            c->udp_my_control_port     = UDP_DEFAULT_CONTROL_PORT;
+            c->udp_server_control_port = UDP_DEFAULT_CONTROL_PORT;
+            c->udp_my_timing_port      = UDP_DEFAULT_TIMING_PORT;
+            c->udp_server_timing_port  = UDP_DEFAULT_TIMING_PORT;
+
+            pa_log_error("aborting RTSP setup, failed creating required sockets");
+
+            break;
+        }
+
+        case STATE_RECORD: {
+            int32_t latency = 0;
+            uint32_t rand;
+            char *alt;
+
+            pa_log_debug("RAOP: RECORD");
+
+            alt = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Latency"));
+            /* Generate a random synchronization source identifier from this session. */
+            pa_random(&rand, sizeof(rand));
+            c->udp_ssrc = rand;
+
+            if (alt)
+                pa_atoi(alt, &latency);
+
+            c->udp_first_packet = true;
+            c->udp_sync_count = 0;
+
+            c->udp_record_callback(c->udp_setup_userdata);
+
+            pa_xfree(alt);
+            break;
+        }
+
+        case STATE_SET_PARAMETER: {
+            pa_log_debug("RAOP: SET_PARAMETER");
+
+            break;
+        }
+
+        case STATE_FLUSH: {
+            pa_log_debug("RAOP: FLUSHED");
+
+            break;
+        }
+
+        case STATE_TEARDOWN: {
+            pa_log_debug("RAOP: TEARDOWN");
+            pa_assert(c->udp_disconnected_callback);
+            pa_assert(c->rtsp);
+
+            pa_rtsp_disconnect(c->rtsp);
+
+            if (c->udp_stream_fd > 0) {
+                pa_close(c->udp_stream_fd);
+                c->udp_stream_fd = -1;
+            }
+
+            pa_log_debug("RTSP control channel closed (teardown)");
+
+            pa_rtsp_client_free(c->rtsp);
+            pa_xfree(c->sid);
+            c->rtsp = NULL;
+            c->sid = NULL;
+
+            /*
+              Callback for cleanup -- e.g. pollfd
+
+              Share the disconnected callback since TEARDOWN event
+              is essentially equivalent to DISCONNECTED.
+              In case some special treatment turns out to be required
+              for TEARDOWN in future, a new callback function may be
+              defined and used.
+            */
+            c->udp_disconnected_callback(c->udp_disconnected_userdata);
+
+            /* Control and timing fds are closed by udp_sink_process_msg,
+               after it disables poll */
+            c->udp_control_fd = -1;
+            c->udp_timing_fd = -1;
+
+            break;
+        }
+
+        case STATE_DISCONNECTED: {
+            pa_log_debug("RAOP: DISCONNECTED");
+            pa_assert(c->udp_disconnected_callback);
+            pa_assert(c->rtsp);
+
+            if (c->udp_stream_fd > 0) {
+                pa_close(c->udp_stream_fd);
+                c->udp_stream_fd = -1;
+            }
+
+            pa_log_debug("RTSP control channel closed (disconnected)");
+
+            pa_rtsp_client_free(c->rtsp);
+            pa_xfree(c->sid);
+            c->rtsp = NULL;
+            c->sid = NULL;
+
+            c->udp_disconnected_callback(c->udp_disconnected_userdata);
+            /* Control and timing fds are closed by udp_sink_process_msg,
+               after it disables poll */
+            c->udp_control_fd = -1;
+            c->udp_timing_fd = -1;
+
+            break;
+        }
+    }
+}
+
+pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_protocol_t protocol) {
     pa_raop_client* c;
+    pa_parsed_address a;
+    pa_sample_spec ss;
 
     pa_assert(core);
     pa_assert(host);
@@ -402,17 +1035,35 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char *host) {
 
     c = pa_xnew0(pa_raop_client, 1);
     c->core = core;
-    c->fd = -1;
+    c->tcp_fd = -1;
+    c->protocol = protocol;
+    c->udp_stream_fd = -1;
+    c->udp_control_fd = -1;
+    c->udp_timing_fd = -1;
+
+    c->udp_my_control_port     = UDP_DEFAULT_CONTROL_PORT;
+    c->udp_server_control_port = UDP_DEFAULT_CONTROL_PORT;
+    c->udp_my_timing_port      = UDP_DEFAULT_TIMING_PORT;
+    c->udp_server_timing_port  = UDP_DEFAULT_TIMING_PORT;
 
     c->host = a.path_or_host;
     if (a.port)
         c->port = a.port;
     else
-        c->port = RAOP_PORT;
+        c->port = DEFAULT_RAOP_PORT;
 
-    if (pa_raop_connect(c)) {
-        pa_raop_client_free(c);
-        return NULL;
+    c->udp_first_packet = true;
+
+    ss = core->default_sample_spec;
+    /* Packet sync interval should be around 1s. */
+    c->udp_sync_interval = ss.rate / UDP_FRAMES_PER_PACKET;
+    c->udp_sync_count = 0;
+
+    if (c->protocol == RAOP_TCP) {
+        if (pa_raop_client_connect(c)) {
+            pa_raop_client_free(c);
+            return NULL;
+        }
     }
 
     return c;
@@ -429,7 +1080,7 @@ void pa_raop_client_free(pa_raop_client *c) {
     pa_xfree(c);
 }
 
-int pa_raop_connect(pa_raop_client *c) {
+int pa_raop_client_connect(pa_raop_client *c) {
     char *sci;
     struct {
         uint32_t a;
@@ -444,7 +1095,10 @@ int pa_raop_connect(pa_raop_client *c) {
         return 0;
     }
 
-    c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, "iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
+    if (c->protocol == RAOP_TCP)
+        c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, "iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
+    else
+        c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, "iTunes/7.6.2 (Windows; N;)");
 
     /* Initialise the AES encryption system. */
     pa_random(c->aes_iv, sizeof(c->aes_iv));
@@ -458,20 +1112,179 @@ int pa_raop_connect(pa_raop_client *c) {
     sci = pa_sprintf_malloc("%08x%08x",rand_data.b, rand_data.c);
     pa_rtsp_add_header(c->rtsp, "Client-Instance", sci);
     pa_xfree(sci);
-    pa_rtsp_set_callback(c->rtsp, rtsp_cb, c);
+    if (c->protocol == RAOP_TCP)
+        pa_rtsp_set_callback(c->rtsp, tcp_rtsp_cb, c);
+    else
+        pa_rtsp_set_callback(c->rtsp, udp_rtsp_cb, c);
 
     return pa_rtsp_connect(c->rtsp);
 }
 
-int pa_raop_flush(pa_raop_client *c) {
+int pa_raop_client_flush(pa_raop_client *c) {
+    int rv = 0;
     pa_assert(c);
 
-    pa_rtsp_flush(c->rtsp, c->seq, c->rtptime);
-    return 0;
+    if (c->rtsp != NULL) {
+        rv = pa_rtsp_flush(c->rtsp, c->seq, c->rtptime);
+        c->udp_sync_count = -1;
+    }
+
+    return rv;
+}
+
+int pa_raop_client_teardown(pa_raop_client *c) {
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (c->rtsp != NULL)
+        rv = pa_rtsp_teardown(c->rtsp);
+
+    return rv;
+}
+
+int pa_raop_client_udp_can_stream(pa_raop_client *c) {
+    int rv = 0;
+
+    pa_assert(c);
+
+    if (c->udp_stream_fd > 0)
+        rv = 1;
+
+    return rv;
+}
+
+int pa_raop_client_udp_handle_timing_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size) {
+    const uint32_t * data = NULL;
+    uint8_t payload = 0;
+    struct timeval tv;
+    uint64_t rci = 0;
+    int rv = 0;
+
+    pa_assert(c);
+    pa_assert(packet);
+
+    /* Timing packets are 32 bytes long: 1 x 8 RTP header (no ssrc) + 3 x 8 NTP timestamps. */
+    if (size != 32 || packet[0] != 0x80)
+    {
+        pa_log_debug("Received an invalid timing packet.");
+        return 1;
+    }
+
+    data = (uint32_t *) (packet + sizeof(udp_timming_header));
+    rci = timeval_to_ntp(pa_rtclock_get(&tv));
+    /* The market bit is always set (see rfc3550 for packet structure) ! */
+    payload = packet[1] ^ 0x80;
+    switch (payload) {
+        case UDP_PAYLOAD_TIMING_REQUEST:
+            rv = udp_send_timing_packet(c, data, rci);
+            break;
+        case UDP_PAYLOAD_TIMING_RESPONSE:
+        default:
+            pa_log_debug("Got an unexpected payload type on timing channel !");
+            return 1;
+    }
+
+    return rv;
+}
+
+int pa_raop_client_udp_handle_control_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size) {
+    uint8_t payload = 0;
+    int rv = 0;
+
+    pa_assert(c);
+    pa_assert(packet);
+
+    if (size != 20 || packet[0] != 0x80)
+    {
+        pa_log_debug("Received an invalid control packet.");
+        return 1;
+    }
+
+    /* The market bit is always set (see rfc3550 for packet structure) ! */
+
+    payload = packet[1] ^ 0x80;
+    switch (payload) {
+        case UDP_PAYLOAD_RETRANSMIT_REQUEST:
+            /* Packet retransmission not implemented yet... */
+            /* rv = ... */
+            break;
+        case UDP_PAYLOAD_RETRANSMIT_REPLY:
+        default:
+            pa_log_debug("Got an unexpected payload type on control channel !");
+            return 1;
+    }
+
+    return rv;
+}
+
+int pa_raop_client_udp_get_blocks_size(pa_raop_client *c, size_t *size) {
+    int rv = 0;
+
+    pa_assert(c);
+    pa_assert(size);
+
+    *size = UDP_FRAMES_PER_PACKET;
+
+    return rv;
+}
+
+ssize_t pa_raop_client_udp_send_audio_packet(pa_raop_client *c, pa_memchunk *block) {
+    uint8_t *buf = NULL;
+    ssize_t len;
+
+    pa_assert(c);
+    pa_assert(block);
+
+    /* Sync RTP & NTP timestamp if required. */
+    if (c->udp_first_packet || c->udp_sync_count >= c->udp_sync_interval) {
+        udp_send_sync_packet(c, c->rtptime);
+        c->udp_sync_count = 0;
+    } else {
+        c->udp_sync_count++;
+    }
+
+    buf = pa_memblock_acquire(block->memblock);
+    pa_assert(buf);
+    pa_assert(block->length > 0);
+    udp_build_audio_header(c, (uint32_t *) (buf + block->index), block->length);
+    len = udp_send_audio_packet(c, buf + block->index, block->length);
+    pa_memblock_release(block->memblock);
+
+    if (len > 0) {
+        pa_assert((size_t) len <= block->length);
+        /* UDP packet has to be sent at once, so it is meaningless to
+           preseve the partial data
+           FIXME: This won't happen at least in *NIX systems?? */
+        if (block->length > (size_t) len) {
+            pa_log_warn("Tried to send %zu bytes but managed to send %zu bytes", block->length, len);
+            len = block->length;
+        }
+        block->index += block->length;
+        block->length = 0;
+    }
+
+    if (c->udp_first_packet)
+        c->udp_first_packet = false;
+
+    return len;
+}
+
+/* Adjust volume so that it fits into VOLUME_DEF <= v <= 0 dB */
+pa_volume_t pa_raop_client_adjust_volume(pa_raop_client *c, pa_volume_t volume) {
+    double minv, maxv;
+
+    if (c->protocol != RAOP_UDP)
+        return volume;
+
+    maxv = pa_sw_volume_from_dB(0.0);
+    minv = maxv * pow(10.0, (double) VOLUME_DEF / 60.0);
+
+    return volume - volume * (minv / maxv) + minv;
 }
 
 int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume) {
-    int rv;
+    int rv = 0;
     double db;
     char *param;
 
@@ -483,10 +1296,13 @@ int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume) {
     else if (db > VOLUME_MAX)
         db = VOLUME_MAX;
 
+    pa_log_debug("volume=%u db=%.6f", volume, db);
+
     param = pa_sprintf_malloc("volume: %0.6f\r\n",  db);
 
     /* We just hit and hope, cannot wait for the callback. */
-    rv = pa_rtsp_setparameter(c->rtsp, param);
+    if (c->rtsp != NULL && pa_rtsp_exec_ready(c->rtsp))
+        rv = pa_rtsp_setparameter(c->rtsp, param);
     pa_xfree(param);
 
     return rv;
@@ -501,21 +1317,23 @@ int pa_raop_client_encode_sample(pa_raop_client *c, pa_memchunk *raw, pa_memchun
     uint8_t *b, *p;
     uint32_t bsize;
     size_t length;
-    static uint8_t header[] = {
-        0x24, 0x00, 0x00, 0x00,
-        0xF0, 0xFF, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00,
-    };
-    int header_size = sizeof(header);
+    const uint8_t *header;
+    int header_size;
 
     pa_assert(c);
-    pa_assert(c->fd > 0);
     pa_assert(raw);
     pa_assert(raw->memblock);
     pa_assert(raw->length > 0);
     pa_assert(encoded);
 
+    if (c->protocol == RAOP_TCP) {
+        header = tcp_audio_header;
+        header_size = sizeof(tcp_audio_header);
+    } else {
+        header = udp_audio_header;
+        header_size = sizeof(udp_audio_header);
+    }
+
     /* We have to send 4 byte chunks */
     bsize = (int)(raw->length / 4);
     length = bsize * 4;
@@ -544,7 +1362,9 @@ int pa_raop_client_encode_sample(pa_raop_client *c, pa_memchunk *raw, pa_memchun
     bit_writer(&bp,&bpos,&size,(bsize>>8)&0xff,8);
     bit_writer(&bp,&bpos,&size,(bsize)&0xff,8);
 
-    ibp = p = pa_memblock_acquire(raw->memblock);
+    p = pa_memblock_acquire(raw->memblock);
+    p += raw->index;
+    ibp = p;
     maxibp = p + raw->length - 4;
     while (ibp <= maxibp) {
         /* Byte swap stereo data. */
@@ -556,16 +1376,22 @@ int pa_raop_client_encode_sample(pa_raop_client *c, pa_memchunk *raw, pa_memchun
         raw->index += 4;
         raw->length -= 4;
     }
+    if (c->protocol == RAOP_UDP)
+        c->rtptime += (ibp - p) / 4;
     pa_memblock_release(raw->memblock);
     encoded->length = header_size + size;
 
-    /* Store the length (endian swapped: make this better). */
-    len = size + header_size - 4;
-    *(b + 2) = len >> 8;
-    *(b + 3) = len & 0xff;
+    if (c->protocol == RAOP_TCP) {
+        /* Store the length (endian swapped: make this better). */
+        len = size + header_size - 4;
+        *(b + 2) = len >> 8;
+        *(b + 3) = len & 0xff;
+    }
 
-    /* Encrypt our data. */
-    aes_encrypt(c, (b + header_size), size);
+    if (c->encryption) {
+        /* Encrypt our data. */
+        aes_encrypt(c, (b + header_size), size);
+    }
 
     /* We're done with the chunk. */
     pa_memblock_release(encoded->memblock);
@@ -573,16 +1399,41 @@ int pa_raop_client_encode_sample(pa_raop_client *c, pa_memchunk *raw, pa_memchun
     return 0;
 }
 
-void pa_raop_client_set_callback(pa_raop_client *c, pa_raop_client_cb_t callback, void *userdata) {
+void pa_raop_client_tcp_set_callback(pa_raop_client *c, pa_raop_client_cb_t callback, void *userdata) {
+    pa_assert(c);
+
+    c->tcp_callback = callback;
+    c->tcp_userdata = userdata;
+}
+
+void pa_raop_client_tcp_set_closed_callback(pa_raop_client *c, pa_raop_client_closed_cb_t callback, void *userdata) {
+    pa_assert(c);
+
+    c->tcp_closed_callback = callback;
+    c->tcp_closed_userdata = userdata;
+}
+
+void pa_raop_client_set_encryption(pa_raop_client *c, int encryption) {
+    c->encryption = encryption;
+}
+
+void pa_raop_client_udp_set_setup_callback(pa_raop_client *c, pa_raop_client_setup_cb_t callback, void *userdata) {
+    pa_assert(c);
+
+    c->udp_setup_callback = callback;
+    c->udp_setup_userdata = userdata;
+}
+
+void pa_raop_client_udp_set_record_callback(pa_raop_client *c, pa_raop_client_record_cb_t callback, void *userdata) {
     pa_assert(c);
 
-    c->callback = callback;
-    c->userdata = userdata;
+    c->udp_record_callback = callback;
+    c->udp_record_userdata = userdata;
 }
 
-void pa_raop_client_set_closed_callback(pa_raop_client *c, pa_raop_client_closed_cb_t callback, void *userdata) {
+void pa_raop_client_udp_set_disconnected_callback(pa_raop_client *c, pa_raop_client_disconnected_cb_t callback, void *userdata) {
     pa_assert(c);
 
-    c->closed_callback = callback;
-    c->closed_userdata = userdata;
+    c->udp_disconnected_callback = callback;
+    c->udp_disconnected_userdata = userdata;
 }
diff --git a/src/modules/raop/raop_client.h b/src/modules/raop/raop_client.h
index 6ba32e9..36be8dc 100644
--- a/src/modules/raop/raop_client.h
+++ b/src/modules/raop/raop_client.h
@@ -20,23 +20,52 @@
   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <pulse/volume.h>
+
 #include <pulsecore/core.h>
+#include <pulsecore/memchunk.h>
+
+typedef enum pa_raop_protocol {
+    RAOP_TCP,
+    RAOP_UDP,
+} pa_raop_protocol_t;
 
 typedef struct pa_raop_client pa_raop_client;
 
-pa_raop_client* pa_raop_client_new(pa_core *core, const char *host);
+pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_protocol_t protocol);
 void pa_raop_client_free(pa_raop_client *c);
 
-int pa_raop_connect(pa_raop_client *c);
-int pa_raop_flush(pa_raop_client *c);
+int pa_raop_client_connect(pa_raop_client *c);
+int pa_raop_client_flush(pa_raop_client *c);
+int pa_raop_client_teardown(pa_raop_client *c);
 
+int pa_raop_client_udp_can_stream(pa_raop_client *c);
+
+void pa_raop_client_set_encryption(pa_raop_client *c, int encryption);
+pa_volume_t pa_raop_client_adjust_volume(pa_raop_client *c, pa_volume_t volume);
 int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume);
 int pa_raop_client_encode_sample(pa_raop_client *c, pa_memchunk *raw, pa_memchunk *encoded);
 
+int pa_raop_client_udp_handle_timing_packet(pa_raop_client *c, const uint8_t packet
+[], ssize_t size);
+int pa_raop_client_udp_handle_control_packet(pa_raop_client *c, const uint8_t packet[], ssize_t size);
+int pa_raop_client_udp_get_blocks_size(pa_raop_client *c, size_t *size);
+ssize_t pa_raop_client_udp_send_audio_packet(pa_raop_client *c, pa_memchunk *block);
+
 typedef void (*pa_raop_client_cb_t)(int fd, void *userdata);
-void pa_raop_client_set_callback(pa_raop_client *c, pa_raop_client_cb_t callback, void *userdata);
+void pa_raop_client_tcp_set_callback(pa_raop_client *c, pa_raop_client_cb_t callback, void *userdata);
 
 typedef void (*pa_raop_client_closed_cb_t)(void *userdata);
-void pa_raop_client_set_closed_callback(pa_raop_client *c, pa_raop_client_closed_cb_t callback, void *userdata);
+void pa_raop_client_tcp_set_closed_callback(pa_raop_client *c, pa_raop_client_closed_cb_t callback, void *userdata);
+
+
+typedef void (*pa_raop_client_setup_cb_t)(int control_fd, int timing_fd, void *userdata);
+void pa_raop_client_udp_set_setup_callback(pa_raop_client *c, pa_raop_client_setup_cb_t callback, void *userdata);
+
+typedef void (*pa_raop_client_record_cb_t)(void *userdata);
+void pa_raop_client_udp_set_record_callback(pa_raop_client *c, pa_raop_client_record_cb_t callback, void *userdata);
+
+typedef void (*pa_raop_client_disconnected_cb_t)(void *userdata);
+void pa_raop_client_udp_set_disconnected_callback(pa_raop_client *c, pa_raop_client_disconnected_cb_t callback, void *userdata);
 
 #endif

commit 43bf134aa16303d04acbf37fb1e69c58f4905943
Author: Hajime Fujita <crisp.fujita at nifty.com>
Date:   Sun Nov 6 12:53:57 2016 -0600

    core-util: do in-place search in pa_str_in_list_spaces
    
    Reviewed-by: Anton Lundin <glance at acc.umu.se>

diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 9d571b8..cd1c96d 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -3022,19 +3022,16 @@ bool pa_str_in_list(const char *haystack, const char *delimiters, const char *ne
 
 /* Checks a whitespace-separated list of words in haystack for needle */
 bool pa_str_in_list_spaces(const char *haystack, const char *needle) {
-    char *s;
+    const char *s;
+    int n;
     const char *state = NULL;
 
     if (!haystack || !needle)
         return false;
 
-    while ((s = pa_split_spaces(haystack, &state))) {
-        if (pa_streq(needle, s)) {
-            pa_xfree(s);
+    while ((s = pa_split_spaces_in_place(haystack, &n, &state))) {
+        if (pa_strneq(needle, s, n))
             return true;
-        }
-
-        pa_xfree(s);
     }
 
     return false;

commit 04b46803cd6c6919757e1a871d5b746a6b277b41
Author: Hajime Fujita <crisp.fujita at nifty.com>
Date:   Sun Nov 6 12:53:56 2016 -0600

    core-util: add pa_split_space_in_place function
    
    Reviewed-by: Anton Lundin <glance at acc.umu.se>

diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index f2999b2..9d571b8 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -1121,6 +1121,25 @@ char *pa_split_spaces(const char *c, const char **state) {
     return pa_xstrndup(current, l);
 }
 
+/* Similar to pa_split_spaces, except this returns a string in-place.
+   Returned string is generally not NULL-terminated.
+   See pa_split_in_place(). */
+const char *pa_split_spaces_in_place(const char *c, int *n, const char **state) {
+    const char *current = *state ? *state : c;
+    size_t l;
+
+    if (!*current || *c == 0)
+        return NULL;
+
+    current += strspn(current, WHITESPACE);
+    l = strcspn(current, WHITESPACE);
+
+    *state = current+l;
+
+    *n = l;
+    return current;
+}
+
 PA_STATIC_TLS_DECLARE(signame, pa_xfree);
 
 /* Return the name of an UNIX signal. Similar to Solaris sig2str() */
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index be023a8..e28b6aa 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -112,6 +112,7 @@ static inline const char *pa_strna(const char *x) {
 char *pa_split(const char *c, const char *delimiters, const char **state);
 const char *pa_split_in_place(const char *c, const char *delimiters, int *n, const char **state);
 char *pa_split_spaces(const char *c, const char **state);
+const char *pa_split_spaces_in_place(const char *c, int *n, const char **state);
 
 char *pa_strip_nl(char *s);
 char *pa_strip(char *s);

commit e2be9fca9c7a018e1ec42b57ba806bf5df2110fb
Author: Hajime Fujita <crisp.fujita at nifty.com>
Date:   Sun Nov 6 12:53:55 2016 -0600

    core-util: add pa_strneq macro
    
    This macro compares if the given two strings, with the maximum length
    of n, are equal. Useful for strings that are not NULL-terminated.
    
    Reviewed-by: Anton Lundin <glance at acc.umu.se>

diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 8847528..be023a8 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -219,6 +219,7 @@ void pa_unset_env_recorded(void);
 bool pa_in_system_mode(void);
 
 #define pa_streq(a,b) (!strcmp((a),(b)))
+#define pa_strneq(a,b,n) (!strncmp((a),(b),(n)))
 
 /* Like pa_streq, but does not blow up on NULL pointers. */
 static inline bool pa_safe_streq(const char *a, const char *b) {

commit 6e6f497219a4b86595a5d4e3ea02cbe2c82ca75d
Author: David Mandelberg <dseomn at google.com>
Date:   Wed Jan 4 11:55:49 2017 -0500

    daemon-conf: add remixing-use-all-sink-channels option
    
    This option controls the PA_RESAMPLER_NO_FILL_SINK flag added in a
    previous commit.
    
    BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=62588
    BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=94563

diff --git a/man/pulse-daemon.conf.5.xml.in b/man/pulse-daemon.conf.5.xml.in
index 1c0de9e..b81a549 100644
--- a/man/pulse-daemon.conf.5.xml.in
+++ b/man/pulse-daemon.conf.5.xml.in
@@ -130,6 +130,14 @@ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
     </option>
 
     <option>
+      <p><opt>remixing-use-all-sink-channels=</opt> If enabled, use
+      all sink channels when remixing. Otherwise, remix to the minimal
+      set of sink channels needed to reproduce all of the source
+      channels. (This has no effect on LFE remixing.) Defaults to
+      <opt>yes</opt>.</p>
+    </option>
+
+    <option>
       <p><opt>enable-lfe-remixing=</opt> If disabled when upmixing or
       downmixing ignore LFE channels. When this option is disabled the
       output LFE channel will only get a signal when an input LFE
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index f0ed0b4..31e4b14 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -82,6 +82,7 @@ static const pa_daemon_conf default_conf = {
     .log_time = false,
     .resample_method = PA_RESAMPLER_AUTO,
     .disable_remixing = false,
+    .remixing_use_all_sink_channels = true,
     .disable_lfe_remixing = true,
     .lfe_crossover_freq = 0,
     .config_file = NULL,
@@ -554,6 +555,8 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
         { "nice-level",                 parse_nice_level,         c, NULL },
         { "disable-remixing",           pa_config_parse_bool,     &c->disable_remixing, NULL },
         { "enable-remixing",            pa_config_parse_not_bool, &c->disable_remixing, NULL },
+        { "remixing-use-all-sink-channels",
+                                        pa_config_parse_bool,     &c->remixing_use_all_sink_channels, NULL },
         { "disable-lfe-remixing",       pa_config_parse_bool,     &c->disable_lfe_remixing, NULL },
         { "enable-lfe-remixing",        pa_config_parse_not_bool, &c->disable_lfe_remixing, NULL },
         { "lfe-crossover-freq",         pa_config_parse_unsigned, &c->lfe_crossover_freq, NULL },
@@ -748,6 +751,7 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
     pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);
     pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));
     pa_strbuf_printf(s, "enable-remixing = %s\n", pa_yes_no(!c->disable_remixing));
+    pa_strbuf_printf(s, "remixing-use-all-sink-channels = %s\n", pa_yes_no(c->remixing_use_all_sink_channels));
     pa_strbuf_printf(s, "enable-lfe-remixing = %s\n", pa_yes_no(!c->disable_lfe_remixing));
     pa_strbuf_printf(s, "lfe-crossover-freq = %u\n", c->lfe_crossover_freq);
     pa_strbuf_printf(s, "default-sample-format = %s\n", pa_sample_format_to_string(c->default_sample_spec.format));
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
index 82b619f..e61f67f 100644
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@ -68,6 +68,7 @@ typedef struct pa_daemon_conf {
         disable_shm,
         disable_memfd,
         disable_remixing,
+        remixing_use_all_sink_channels,
         disable_lfe_remixing,
         load_default_script_file,
         disallow_exit,
diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in
index f3a1cc3..2d74afa 100644
--- a/src/daemon/daemon.conf.in
+++ b/src/daemon/daemon.conf.in
@@ -55,6 +55,7 @@ ifelse(@HAVE_DBUS@, 1, [dnl
 
 ; resample-method = speex-float-1
 ; enable-remixing = yes
+; remixing-use-all-sink-channels = yes
 ; enable-lfe-remixing = no
 ; lfe-crossover-freq = 0
 
diff --git a/src/daemon/main.c b/src/daemon/main.c
index dc5e5bc..280252a 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -1037,6 +1037,7 @@ int main(int argc, char *argv[]) {
     c->realtime_priority = conf->realtime_priority;
     c->realtime_scheduling = conf->realtime_scheduling;
     c->disable_remixing = conf->disable_remixing;
+    c->remixing_use_all_sink_channels = conf->remixing_use_all_sink_channels;
     c->disable_lfe_remixing = conf->disable_lfe_remixing;
     c->deferred_volume = conf->deferred_volume;
     c->running_as_daemon = conf->daemonize;
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 2a96dfa..afdb0a4 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -142,6 +142,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t
     c->realtime_scheduling = false;
     c->realtime_priority = 5;
     c->disable_remixing = false;
+    c->remixing_use_all_sink_channels = true;
     c->disable_lfe_remixing = true;
     c->lfe_crossover_freq = 0;
     c->deferred_volume = true;
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 802111b..d2fe887 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -200,6 +200,7 @@ struct pa_core {
     bool running_as_daemon:1;
     bool realtime_scheduling:1;
     bool disable_remixing:1;
+    bool remixing_use_all_sink_channels:1;
     bool disable_lfe_remixing:1;
     bool deferred_volume:1;
 
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index e9694f2..b937383 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -456,6 +456,7 @@ int pa_sink_input_new(
                           ((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
                           ((data->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
                           (core->disable_remixing || (data->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+                          (core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) |
                           (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
                 pa_log_warn("Unsupported resampling operation.");
                 return -PA_ERR_NOTSUPPORTED;
@@ -2259,6 +2260,7 @@ int pa_sink_input_update_rate(pa_sink_input *i) {
                                      ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
                                      ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
                                      (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+                                     (i->core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) |
                                      (i->core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0));
 
         if (!new_resampler) {
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 6714ea9..0ba19c8 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -401,6 +401,7 @@ int pa_source_output_new(
                         ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
                         ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
                         (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+                        (core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) |
                         (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
                 pa_log_warn("Unsupported resampling operation.");
                 return -PA_ERR_NOTSUPPORTED;
@@ -1714,6 +1715,7 @@ int pa_source_output_update_rate(pa_source_output *o) {
                                      ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
                                      ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
                                      (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+                                     (o->core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) |
                                      (o->core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0));
 
         if (!new_resampler) {

commit 6ec4ca218e0cca8ef9f0785736edf04ada4c0b7b
Author: David Mandelberg <dseomn at google.com>
Date:   Wed Jan 4 11:55:48 2017 -0500

    remix-test: test the remixer with PA_RESAMPLER_NO_FILL_SINK set

diff --git a/src/tests/remix-test.c b/src/tests/remix-test.c
index 5b0c677..0dcc2f1 100644
--- a/src/tests/remix-test.c
+++ b/src/tests/remix-test.c
@@ -61,6 +61,8 @@ int main(int argc, char *argv[]) {
         RESAMPLE_FLAGS(PA_RESAMPLER_NO_REMAP),
         RESAMPLE_FLAGS(PA_RESAMPLER_NO_REMIX),
         RESAMPLE_FLAGS(PA_RESAMPLER_NO_LFE),
+        RESAMPLE_FLAGS(PA_RESAMPLER_NO_FILL_SINK),
+        RESAMPLE_FLAGS(PA_RESAMPLER_NO_LFE | PA_RESAMPLER_NO_FILL_SINK),
         { .str = NULL, .value = 0 },
     };
 

commit 21c3570b120eef69dfb7bc426fca047be20c6a1e
Author: David Mandelberg <dseomn at google.com>
Date:   Wed Jan 4 11:55:47 2017 -0500

    resampler: Flag for remixing to all sink channels.
    
    Add a flag PA_RESAMPLER_NO_FILL_SINK, which controls whether remixing
    should attempt to use all sink channels, versus only the ones needed
    to reproduce the source audio.
    
    BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=62588
    BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=94563
    
    Suggested-by: Alexander E. Patrakov <patrakov at gmail.com>

diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index ea22cd2..063c4c5 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -796,6 +796,64 @@ static int front_rear_side(pa_channel_position_t p) {
     return ON_OTHER;
 }
 
+/* Fill a map of which output channels should get mono from input, not including
+ * LFE output channels. (The LFE output channels are mapped separately.)
+ */
+static void setup_oc_mono_map(const pa_resampler *r, float *oc_mono_map) {
+    unsigned oc;
+    unsigned n_oc;
+    bool found_oc_for_mono = false;
+
+    pa_assert(r);
+    pa_assert(oc_mono_map);
+
+    n_oc = r->o_ss.channels;
+
+    if (!(r->flags & PA_RESAMPLER_NO_FILL_SINK)) {
+        /* Mono goes to all non-LFE output channels and we're done. */
+        for (oc = 0; oc < n_oc; oc++)
+            oc_mono_map[oc] = on_lfe(r->o_cm.map[oc]) ? 0.0f : 1.0f;
+        return;
+    } else {
+        /* Initialize to all zero so we can select individual channels below. */
+        for (oc = 0; oc < n_oc; oc++)
+            oc_mono_map[oc] = 0.0f;
+    }
+
+    for (oc = 0; oc < n_oc; oc++) {
+        if (r->o_cm.map[oc] == PA_CHANNEL_POSITION_MONO) {
+            oc_mono_map[oc] = 1.0f;
+            found_oc_for_mono = true;
+        }
+    }
+    if (found_oc_for_mono)
+        return;
+
+    for (oc = 0; oc < n_oc; oc++) {
+        if (r->o_cm.map[oc] == PA_CHANNEL_POSITION_FRONT_CENTER) {
+            oc_mono_map[oc] = 1.0f;
+            found_oc_for_mono = true;
+        }
+    }
+    if (found_oc_for_mono)
+        return;
+
+    for (oc = 0; oc < n_oc; oc++) {
+        if (r->o_cm.map[oc] == PA_CHANNEL_POSITION_FRONT_LEFT || r->o_cm.map[oc] == PA_CHANNEL_POSITION_FRONT_RIGHT) {
+            oc_mono_map[oc] = 1.0f;
+            found_oc_for_mono = true;
+        }
+    }
+    if (found_oc_for_mono)
+        return;
+
+    /* Give up on finding a suitable map for mono, and just send it to all
+     * non-LFE output channels.
+     */
+    for (oc = 0; oc < n_oc; oc++)
+        oc_mono_map[oc] = on_lfe(r->o_cm.map[oc]) ? 0.0f : 1.0f;
+}
+
 static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed) {
     unsigned oc, ic;
     unsigned n_oc, n_ic;
@@ -858,14 +916,14 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed)
          * 1) Connect all channels with matching names.
          *
          * 2) Mono Handling:
-         *    S:Mono: Copy into all D:channels
+         *    S:Mono: See setup_oc_mono_map().
          *    D:Mono: Avg all S:channels
          *
-         * 3) Mix D:Left, D:Right:
+         * 3) Mix D:Left, D:Right (if PA_RESAMPLER_NO_FILL_SINK is clear):
          *    D:Left: If not connected, avg all S:Left
          *    D:Right: If not connected, avg all S:Right
          *
-         * 4) Mix D:Center
+         * 4) Mix D:Center (if PA_RESAMPLER_NO_FILL_SINK is clear):
          *    If not connected, avg all S:Center
          *    If still not connected, avg all S:Left, S:Right
          *
@@ -908,6 +966,7 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed)
             ic_unconnected_center = 0,
             ic_unconnected_lfe = 0;
         bool ic_unconnected_center_mixed_in = 0;
+        float oc_mono_map[PA_CHANNELS_MAX];
 
         for (ic = 0; ic < n_ic; ic++) {
             if (on_left(r->i_cm.map[ic]))
@@ -918,6 +977,8 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed)
                 ic_center++;
         }
 
+        setup_oc_mono_map(r, oc_mono_map);
+
         for (oc = 0; oc < n_oc; oc++) {
             bool oc_connected = false;
             pa_channel_position_t b = r->o_cm.map[oc];
@@ -925,14 +986,17 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed)
             for (ic = 0; ic < n_ic; ic++) {
                 pa_channel_position_t a = r->i_cm.map[ic];
 
-                if (a == b || a == PA_CHANNEL_POSITION_MONO) {
+                if (a == b) {
                     m->map_table_f[oc][ic] = 1.0f;
 
                     oc_connected = true;
                     ic_connected[ic] = true;
+                }
+                else if (a == PA_CHANNEL_POSITION_MONO && oc_mono_map[oc] > 0.0f) {
+                    m->map_table_f[oc][ic] = oc_mono_map[oc];
 
-                    if (a == PA_CHANNEL_POSITION_MONO && on_lfe(b) && !(r->flags & PA_RESAMPLER_NO_LFE))
-                        *lfe_remixed = true;
+                    oc_connected = true;
+                    ic_connected[ic] = true;
                 }
                 else if (b == PA_CHANNEL_POSITION_MONO) {
                     m->map_table_f[oc][ic] = 1.0f / (float) n_ic;
@@ -945,7 +1009,7 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed)
             if (!oc_connected) {
                 /* Try to find matching input ports for this output port */
 
-                if (on_left(b)) {
+                if (on_left(b) && !(r->flags & PA_RESAMPLER_NO_FILL_SINK)) {
 
                     /* We are not connected and on the left side, let's
                      * average all left side input channels. */
@@ -960,7 +1024,7 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed)
                     /* We ignore the case where there is no left input channel.
                      * Something is really wrong in this case anyway. */
 
-                } else if (on_right(b)) {
+                } else if (on_right(b) && !(r->flags & PA_RESAMPLER_NO_FILL_SINK)) {
 
                     /* We are not connected and on the right side, let's
                      * average all right side input channels. */
@@ -976,7 +1040,7 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed)
                      * channel. Something is really wrong in this case anyway.
                      * */
 
-                } else if (on_center(b)) {
+                } else if (on_center(b) && !(r->flags & PA_RESAMPLER_NO_FILL_SINK)) {
 
                     if (ic_center > 0) {
 
diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h
index 4469022..5d3171f 100644
--- a/src/pulsecore/resampler.h
+++ b/src/pulsecore/resampler.h
@@ -68,7 +68,8 @@ typedef enum pa_resample_flags {
     PA_RESAMPLER_VARIABLE_RATE = 0x0001U,
     PA_RESAMPLER_NO_REMAP      = 0x0002U,  /* implies NO_REMIX */
     PA_RESAMPLER_NO_REMIX      = 0x0004U,
-    PA_RESAMPLER_NO_LFE        = 0x0008U
+    PA_RESAMPLER_NO_LFE        = 0x0008U,
+    PA_RESAMPLER_NO_FILL_SINK  = 0x0010U,
 } pa_resample_flags_t;
 
 struct pa_resampler {

commit e2968b573841e7b0ae8429d2b1ab54217f3b9055
Author: David Mandelberg <dseomn at google.com>
Date:   Wed Jan 4 11:55:46 2017 -0500

    remix-test: test the remixer using different flags
    
    This will make it easier to see how the following commit affects the
    remixer.

diff --git a/src/tests/remix-test.c b/src/tests/remix-test.c
index 1e58418..5b0c677 100644
--- a/src/tests/remix-test.c
+++ b/src/tests/remix-test.c
@@ -27,6 +27,17 @@
 #include <pulsecore/macro.h>
 #include <pulsecore/memblock.h>
 
+struct resample_flags {
+    const char *str;
+    pa_resample_flags_t value;
+};
+
+/* Call like this to get an initializer for struct resample_flags:
+ *     RESAMPLE_FLAGS(PA_RESAMPLER_NO_LFE)
+ */
+#define RESAMPLE_FLAGS(flags) { .str = #flags, .value = (flags) }
+
+
 int main(int argc, char *argv[]) {
 
     static const pa_channel_map maps[] = {
@@ -45,7 +56,15 @@ int main(int argc, char *argv[]) {
         { 0, { 0 } }
     };
 
-    unsigned i, j;
+    static const struct resample_flags flag_sets[] = {
+        RESAMPLE_FLAGS(0),
+        RESAMPLE_FLAGS(PA_RESAMPLER_NO_REMAP),
+        RESAMPLE_FLAGS(PA_RESAMPLER_NO_REMIX),
+        RESAMPLE_FLAGS(PA_RESAMPLER_NO_LFE),
+        { .str = NULL, .value = 0 },
+    };
+
+    unsigned i, j, k;
     pa_mempool *pool;
     unsigned crossover_freq = 120;
 
@@ -59,20 +78,24 @@ int main(int argc, char *argv[]) {
             pa_resampler *r;
             pa_sample_spec ss1, ss2;
 
-            pa_log_info("Converting from '%s' to '%s'.", pa_channel_map_snprint(a, sizeof(a), &maps[i]), pa_channel_map_snprint(b, sizeof(b), &maps[j]));
-
             ss1.channels = maps[i].channels;
             ss2.channels = maps[j].channels;
 
             ss1.rate = ss2.rate = 44100;
             ss1.format = ss2.format = PA_SAMPLE_S16NE;
 
-            r = pa_resampler_new(pool, &ss1, &maps[i], &ss2, &maps[j], crossover_freq, PA_RESAMPLER_AUTO, 0);
+            for (k = 0; flag_sets[k].str; k++) {
+                pa_log_info("Converting from '%s' to '%s' with flags %s.", pa_channel_map_snprint(a, sizeof(a), &maps[i]),
+                            pa_channel_map_snprint(b, sizeof(b), &maps[j]), flag_sets[k].str);
+
+                r = pa_resampler_new(pool, &ss1, &maps[i], &ss2, &maps[j], crossover_freq, PA_RESAMPLER_AUTO,
+                                     flag_sets[k].value);
 
-            /* We don't really care for the resampler. We just want to
-             * see the remixing debug output. */
+                /* We don't really care for the resampler. We just want to
+                 * see the remixing debug output. */
 
-            pa_resampler_free(r);
+                pa_resampler_free(r);
+            }
         }
 
     pa_mempool_unref(pool);

commit 94f32ebfcd51b2e13149a95318a497c05c438573
Author: Corentin Noël <corentin at elementary.io>
Date:   Mon Dec 26 11:52:06 2016 +0100

    vala: use the correct syntax
    
    Constants should be declared simply with "const". With struct members,
    "static" means that all struct instances share the same variable, i.e.
    all instances always see the same value. That's of course already
    implied in the concept of "constant". Newer Vala versions don't allow
    mixing "const" and "static".

diff --git a/vala/libpulse.vapi b/vala/libpulse.vapi
index 207cedf..107b745 100644
--- a/vala/libpulse.vapi
+++ b/vala/libpulse.vapi
@@ -110,7 +110,7 @@ namespace PulseAudio {
                 public uint8 channels;
 
                 [CCode (cname="PA_SAMPLE_SPEC_SNPRINT_MAX")]
-                public static const size_t SNPRINT_MAX;
+                public const size_t SNPRINT_MAX;
 
                 [CCode (cname="pa_bytes_per_second")]
                 public size_t bytes_per_second();
@@ -170,23 +170,23 @@ namespace PulseAudio {
         public struct Volume : uint32 {
 
                 [CCode (cname="PA_SW_VOLUME_SNPRINT_DB_MAX")]
-                public static const size_t SW_SNPRINT_DB_MAX;
+                public const size_t SW_SNPRINT_DB_MAX;
 
                 [CCode (cname="PA_VOLUME_SNPRINT_MAX")]
-                public static const size_t SNPRINT_MAX;
+                public const size_t SNPRINT_MAX;
 
                 [CCode (cname="PA_VOLUME_MAX")]
-                public static const Volume MAX;
+                public const Volume MAX;
 
                 [CCode (cname="PA_VOLUME_NORM")]
-                public static const Volume NORM;
+                public const Volume NORM;
 
                 [CCode (cname="PA_VOLUME_MUTED")]
-                public static const Volume MUTED;
+                public const Volume MUTED;
 
                 // [CCode (cname="PA_VOLUME_INVALID")]
                 [CCode (cname="PA_VOLUME_MAX")]
-                public static const Volume INVALID;
+                public const Volume INVALID;
 
                 [CCode (cname="pa_volume_snprint", instance_pos = 3.1)]
                 public unowned string snprint(char[] s);
@@ -245,10 +245,10 @@ namespace PulseAudio {
                 public Volume values[32];
 
                 [CCode (cname="PA_SW_CVOLUME_SNPRINT_DB_MAX")]
-                public static const size_t SW_SNPRINT_DB_MAX;
+                public const size_t SW_SNPRINT_DB_MAX;
 
                 [CCode (cname="PA_CVOLUME_SNPRINT_MAX")]
-                public static const size_t SNPRINT_MAX;
+                public const size_t SNPRINT_MAX;
 
                 [CCode (cname="pa_cvolume_equal")]
                 public bool equal(CVolume other);
@@ -382,7 +382,7 @@ namespace PulseAudio {
                 public ChannelPosition map[32];
 
                 [CCode (cname="PA_CHANNEL_MAP_SNPRINT_MAX")]
-                public static const size_t SNPRINT_MAX;
+                public const size_t SNPRINT_MAX;
 
                 [CCode (cname="pa_channel_map_init")]
                 public ChannelMap();
@@ -532,121 +532,121 @@ namespace PulseAudio {
         public class Proplist {
 
                 [CCode (cname="PA_PROP_MEDIA_NAME")]
-                public static const string PROP_MEDIA_NAME;
+                public const string PROP_MEDIA_NAME;
                 [CCode (cname="PA_PROP_MEDIA_TITLE")]
-                public static const string PROP_MEDIA_TITLE;
+                public const string PROP_MEDIA_TITLE;
                 [CCode (cname="PA_PROP_MEDIA_ARTIST")]
-                public static const string PROP_MEDIA_ARTIST;
+                public const string PROP_MEDIA_ARTIST;
                 [CCode (cname="PA_PROP_MEDIA_COPYRIGHT")]
-                public static const string PROP_MEDIA_COPYRIGHT;
+                public const string PROP_MEDIA_COPYRIGHT;
                 [CCode (cname="PA_PROP_MEDIA_SOFTWARE")]
-                public static const string PROP_MEDIA_SOFTWARE;
+                public const string PROP_MEDIA_SOFTWARE;
                 [CCode (cname="PA_PROP_MEDIA_LANGUAGE")]
-                public static const string PROP_MEDIA_LANGUAGE;
+                public const string PROP_MEDIA_LANGUAGE;
                 [CCode (cname="PA_PROP_MEDIA_FILENAME")]
-                public static const string PROP_MEDIA_FILENAME;
+                public const string PROP_MEDIA_FILENAME;
                 [CCode (cname="PA_PROP_MEDIA_ICON_NAME")]
-                public static const string PROP_MEDIA_ICON_NAME;
+                public const string PROP_MEDIA_ICON_NAME;
                 [CCode (cname="PA_PROP_MEDIA_ROLE")]
-                public static const string PROP_MEDIA_ROLE;
+                public const string PROP_MEDIA_ROLE;
                 [CCode (cname="PA_PROP_EVENT_ID")]
-                public static const string PROP_EVENT_ID;
+                public const string PROP_EVENT_ID;
                 [CCode (cname="PA_PROP_EVENT_DESCRIPTION")]
-                public static const string PROP_EVENT_DESCRIPTION;
+                public const string PROP_EVENT_DESCRIPTION;
                 [CCode (cname="PA_PROP_EVENT_MOUSE_X")]
-                public static const string PROP_EVENT_MOUSE_X;
+                public const string PROP_EVENT_MOUSE_X;
                 [CCode (cname="PA_PROP_EVENT_MOUSE_Y")]
-                public static const string PROP_EVENT_MOUSE_Y;
+                public const string PROP_EVENT_MOUSE_Y;
                 [CCode (cname="PA_PROP_EVENT_MOUSE_HPOS")]
-                public static const string PROP_EVENT_MOUSE_HPOS;
+                public const string PROP_EVENT_MOUSE_HPOS;
                 [CCode (cname="PA_PROP_EVENT_MOUSE_VPOS")]
-                public static const string PROP_EVENT_MOUSE_VPOS;
+                public const string PROP_EVENT_MOUSE_VPOS;
                 [CCode (cname="PA_PROP_EVENT_MOUSE_BUTTON")]
-                public static const string PROP_EVENT_MOUSE_BUTTON;
+                public const string PROP_EVENT_MOUSE_BUTTON;
                 [CCode (cname="PA_PROP_WINDOW_NAME")]
-                public static const string PROP_WINDOW_NAME;
+                public const string PROP_WINDOW_NAME;
                 [CCode (cname="PA_PROP_WINDOW_ID")]
-                public static const string PROP_WINDOW_ID;
+                public const string PROP_WINDOW_ID;
                 [CCode (cname="PA_PROP_WINDOW_ICON_NAME")]
-                public static const string PROP_WINDOW_ICON_NAME;
+                public const string PROP_WINDOW_ICON_NAME;
                 [CCode (cname="PA_PROP_WINDOW_X11_DISPLAY")]
-                public static const string PROP_WINDOW_X11_DISPLAY;
+                public const string PROP_WINDOW_X11_DISPLAY;
                 [CCode (cname="PA_PROP_WINDOW_X11_SCREEN")]
-                public static const string PROP_WINDOW_X11_SCREEN;
+                public const string PROP_WINDOW_X11_SCREEN;
                 [CCode (cname="PA_PROP_WINDOW_X11_MONITOR")]
-                public static const string PROP_WINDOW_X11_MONITOR;
+                public const string PROP_WINDOW_X11_MONITOR;
                 [CCode (cname="PA_PROP_WINDOW_X11_XID")]
-                public static const string PROP_WINDOW_X11_XID;
+                public const string PROP_WINDOW_X11_XID;
                 [CCode (cname="PA_PROP_APPLICATION_NAME")]
-                public static const string PROP_APPLICATION_NAME;
+                public const string PROP_APPLICATION_NAME;
                 [CCode (cname="PA_PROP_APPLICATION_ID")]
-                public static const string PROP_APPLICATION_ID;
+                public const string PROP_APPLICATION_ID;
                 [CCode (cname="PA_PROP_APPLICATION_VERSION")]
-                public static const string PROP_APPLICATION_VERSION;
+                public const string PROP_APPLICATION_VERSION;
                 [CCode (cname="PA_PROP_APPLICATION_ICON_NAME")]
-                public static const string PROP_APPLICATION_ICON_NAME;
+                public const string PROP_APPLICATION_ICON_NAME;
                 [CCode (cname="PA_PROP_APPLICATION_LANGUAGE")]
-                public static const string PROP_APPLICATION_LANGUAGE;
+                public const string PROP_APPLICATION_LANGUAGE;
                 [CCode (cname="PA_PROP_APPLICATION_PROCESS_ID")]
-                public static const string PROP_APPLICATION_PROCESS_ID;
+                public const string PROP_APPLICATION_PROCESS_ID;
                 [CCode (cname="PA_PROP_APPLICATION_PROCESS_BINARY")]
-                public static const string PROP_APPLICATION_PROCESS_BINARY;
+                public const string PROP_APPLICATION_PROCESS_BINARY;
                 [CCode (cname="PA_PROP_APPLICATION_PROCESS_USER")]
-                public static const string PROP_APPLICATION_PROCESS_USER;
+                public const string PROP_APPLICATION_PROCESS_USER;
                 [CCode (cname="PA_PROP_APPLICATION_PROCESS_HOST")]
-                public static const string PROP_APPLICATION_PROCESS_HOST;
+                public const string PROP_APPLICATION_PROCESS_HOST;
                 [CCode (cname="PA_PROP_APPLICATION_PROCESS_MACHINE_ID")]
-                public static const string PROP_APPLICATION_PROCESS_MACHINE_ID;
+                public const string PROP_APPLICATION_PROCESS_MACHINE_ID;
                 [CCode (cname="PA_PROP_APPLICATION_PROCESS_SESSION_ID")]
-                public static const string PROP_APPLICATION_PROCESS_SESSION_ID;
+                public const string PROP_APPLICATION_PROCESS_SESSION_ID;
                 [CCode (cname="PA_PROP_DEVICE_STRING")]
-                public static const string PROP_DEVICE_STRING;
+                public const string PROP_DEVICE_STRING;
                 [CCode (cname="PA_PROP_DEVICE_API")]
-                public static const string PROP_DEVICE_API;
+                public const string PROP_DEVICE_API;
                 [CCode (cname="PA_PROP_DEVICE_DESCRIPTION")]
-                public static const string PROP_DEVICE_DESCRIPTION;
+                public const string PROP_DEVICE_DESCRIPTION;
                 [CCode (cname="PA_PROP_DEVICE_BUS_PATH")]
-                public static const string PROP_DEVICE_BUS_PATH;
+                public const string PROP_DEVICE_BUS_PATH;
                 [CCode (cname="PA_PROP_DEVICE_SERIAL")]
-                public static const string PROP_DEVICE_SERIAL;
+                public const string PROP_DEVICE_SERIAL;
                 [CCode (cname="PA_PROP_DEVICE_VENDOR_ID")]
-                public static const string PROP_DEVICE_VENDOR_ID;
+                public const string PROP_DEVICE_VENDOR_ID;
                 [CCode (cname="PA_PROP_DEVICE_VENDOR_NAME")]
-                public static const string PROP_DEVICE_VENDOR_NAME;
+                public const string PROP_DEVICE_VENDOR_NAME;
                 [CCode (cname="PA_PROP_DEVICE_PRODUCT_ID")]
-                public static const string PROP_DEVICE_PRODUCT_ID;
+                public const string PROP_DEVICE_PRODUCT_ID;
                 [CCode (cname="PA_PROP_DEVICE_PRODUCT_NAME")]
-                public static const string PROP_DEVICE_PRODUCT_NAME;
+                public const string PROP_DEVICE_PRODUCT_NAME;
                 [CCode (cname="PA_PROP_DEVICE_CLASS")]
-                public static const string PROP_DEVICE_CLASS;
+                public const string PROP_DEVICE_CLASS;
                 [CCode (cname="PA_PROP_DEVICE_FORM_FACTOR")]
-                public static const string PROP_DEVICE_FORM_FACTOR;
+                public const string PROP_DEVICE_FORM_FACTOR;
                 [CCode (cname="PA_PROP_DEVICE_BUS")]
-                public static const string PROP_DEVICE_BUS;
+                public const string PROP_DEVICE_BUS;
                 [CCode (cname="PA_PROP_DEVICE_ICON_NAME")]
-                public static const string PROP_DEVICE_ICON_NAME;
+                public const string PROP_DEVICE_ICON_NAME;
                 [CCode (cname="PA_PROP_DEVICE_ACCESS_MODE")]
-                public static const string PROP_DEVICE_ACCESS_MODE;
+                public const string PROP_DEVICE_ACCESS_MODE;
                 [CCode (cname="PA_PROP_DEVICE_MASTER_DEVICE")]
-                public static const string PROP_DEVICE_MASTER_DEVICE;
+                public const string PROP_DEVICE_MASTER_DEVICE;
                 [CCode (cname="PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE")]
-                public static const string PROP_DEVICE_BUFFERING_BUFFER_SIZE;
+                public const string PROP_DEVICE_BUFFERING_BUFFER_SIZE;
                 [CCode (cname="PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE")]
-                public static const string PROP_DEVICE_BUFFERING_FRAGMENT_SIZE;
+                public const string PROP_DEVICE_BUFFERING_FRAGMENT_SIZE;
                 [CCode (cname="PA_PROP_DEVICE_PROFILE_NAME")]
-                public static const string PROP_DEVICE_PROFILE_NAME;
+                public const string PROP_DEVICE_PROFILE_NAME;
                 [CCode (cname="PA_PROP_DEVICE_INTENDED_ROLES")]
-                public static const string PROP_DEVICE_INTENDED_ROLES;
+                public const string PROP_DEVICE_INTENDED_ROLES;
                 [CCode (cname="PA_PROP_DEVICE_PROFILE_DESCRIPTION")]
-                public static const string PROP_DEVICE_PROFILE_DESCRIPTION;
+                public const string PROP_DEVICE_PROFILE_DESCRIPTION;
                 [CCode (cname="PA_PROP_MODULE_AUTHOR")]
-                public static const string PROP_MODULE_AUTHOR;
+                public const string PROP_MODULE_AUTHOR;
                 [CCode (cname="PA_PROP_MODULE_DESCRIPTION")]
-                public static const string PROP_MODULE_DESCRIPTION;
+                public const string PROP_MODULE_DESCRIPTION;
                 [CCode (cname="PA_PROP_MODULE_USAGE")]
-                public static const string PROP_MODULE_USAGE;
+                public const string PROP_MODULE_USAGE;
                 [CCode (cname="PA_PROP_MODULE_VERSION")]
-                public static const string PROP_MODULE_VERSION;
+                public const string PROP_MODULE_VERSION;
 
                 [CCode (cname="pa_proplist_new")]
                 public Proplist();

commit 5287f09f067e81f3fd3c2d781a60e64cc0619f6c
Author: Takashi Sakamoto <o-takashi at sakamocchi.jp>
Date:   Fri Dec 30 12:05:20 2016 +0900

    alsa: remove double calls of snd_pcm_prepare()
    
    In alsa-lib, snd_pcm_hw_params() internally calls snd_pcm_prepare(), thus
    user space applications have no need to call snd_pcm_prepare() after calls
    of snd_pcm_hw_params(). An explicit calls of snd_pcm_prepare() is expected
    in a case to recover PCM substreams.
    
    Current implementation of PulseAudio modules for ALSA playbacking/capturing
    results in double calls of snd_pcm_prepare(). The second call for hw plugin
    of alsa-lib executes ioctl(2) with SNDRV_PCM_IOCTL_PREPARE command in state
    of SNDRV_PCM_STATE_PREPARED for the PCM substream. This has no effects to
    the PCM substream as long as corresponding drivers are implemented
    correctly.
    
    This commit removes the second call for the reason.
    
    Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index dfb2aa7..63d370f 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -410,11 +410,6 @@ success:
     if (ss->format != _ss.format)
         pa_log_info("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(_ss.format));
 
-    if ((ret = snd_pcm_prepare(pcm_handle)) < 0) {
-        pa_log_info("snd_pcm_prepare() failed: %s", pa_alsa_strerror(ret));
-        goto finish;
-    }
-
     if ((ret = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) {
         pa_log_info("snd_pcm_hw_params_current() failed: %s", pa_alsa_strerror(ret));
         goto finish;
diff --git a/src/tests/alsa-time-test.c b/src/tests/alsa-time-test.c
index a877cb8..dff95bb 100644
--- a/src/tests/alsa-time-test.c
+++ b/src/tests/alsa-time-test.c
@@ -132,9 +132,6 @@ int main(int argc, char *argv[]) {
     r = snd_pcm_sw_params(pcm, swparams);
     assert(r == 0);
 
-    r = snd_pcm_prepare(pcm);
-    assert(r == 0);
-
     r = snd_pcm_sw_params_current(pcm, swparams);
     assert(r == 0);
 

commit fb6b8dfdec2df39c055f98266aa1eb75e8ebfb1c
Author: Renjith Thomas <renjith at lightlevitate.com>
Date:   Thu Dec 15 12:34:14 2016 +0530

    bluetooth: fix distorted audio during Bluetooth SCO HFP/HSP playback
    
    Issue: When HFP/HSP profile is used with certain BT chipsets, the
    audio sounds heavily distorted, with very slow playback full of noise.
    During recording, the samples are dropped and it distorts the recorded
    audio samples.
    
    The root cause of both the issues are related to the fixed MTU sizes
    in the PA stack, which is 48 bytes. Here, the BT chipset CC256x had
    180 bytes MTU and it was being under-utilized and the rate at which
    the samples were being accepted where not matching the expected rate,
    and hence the distortion.
    
    Solution: The appropriate solution to this problem is by reading the
    MTU size of the SCO socket using getsockopts dynamically.
    
    BugLink: http://bit.ly/2gDpGPv
    BugLink: http://bit.ly/2hQsARK

diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index cf88126..4974c3f 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -103,6 +103,8 @@ static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_backend *backend, D
 static int bluez5_sco_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
     pa_bluetooth_device *d = t->device;
     struct sockaddr_sco addr;
+    struct sco_options sco_opt;
+    socklen_t len;
     int err, i;
     int sock;
     bdaddr_t src;
@@ -124,34 +126,40 @@ static int bluez5_sco_acquire_cb(pa_bluetooth_transport *t, bool optional, size_
         return -1;
     }
 
-    memset(&addr, 0, sizeof(addr));
+    len = sizeof(addr);
+    memset(&addr, 0, len);
     addr.sco_family = AF_BLUETOOTH;
     bacpy(&addr.sco_bdaddr, &src);
 
-    if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+    if (bind(sock, (struct sockaddr *) &addr, len) < 0) {
         pa_log_error("bind(): %s", pa_cstrerror(errno));
         goto fail_close;
     }
 
-    memset(&addr, 0, sizeof(addr));
+    memset(&addr, 0, len);
     addr.sco_family = AF_BLUETOOTH;
     bacpy(&addr.sco_bdaddr, &dst);
 
     pa_log_info("doing connect");
-    err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+    err = connect(sock, (struct sockaddr *) &addr, len);
     if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) {
         pa_log_error("connect(): %s", pa_cstrerror(errno));
         goto fail_close;
     }
 
-    /* The "48" below is hardcoded until we get meaningful MTU values exposed
-     * by the kernel */
+    len = sizeof(sco_opt);
+    memset(&sco_opt, 0, len);
 
-    if (imtu)
-        *imtu = 48;
+    if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+        pa_log_warn("getsockopt(SCO_OPTIONS) failed, loading defaults");
 
-    if (omtu)
-        *omtu = 48;
+        /* Setting defaults in case of error */
+        if (imtu) *imtu = 48;
+        if (omtu) *omtu = 48;
+    } else {
+        if (imtu) *imtu = sco_opt.mtu;
+        if (omtu) *omtu = sco_opt.mtu;
+    }
 
     return sock;
 

commit 9ce55be61047d685ccd0a141decf823fa68c62df
Author: Felipe Sateler <fsateler at debian.org>
Date:   Mon Nov 28 14:49:06 2016 -0300

    padsp: allow overriding library install location
    
    The current build script hardcodes the $pkglibdir in the padsp command.
    This works and is a reasonable default. However, distributions that
    know where they install, can override this path and thus make padsp
    work for any architecture that has the library installed by using the
    following configure argument:
    
       --with-pulsedsp-location='/usr/\\$$LIB/pulseaudio'
    
    This works because ld.so considers $LIB a variable that will expand to
    several location paths, depending on the architecture of the binary
    being executed.
    
    In debian, for example, this would work for libpulsedsp.so installed in
    /usr/lib/x86_64-linux-gnu/ for amd64 and /usr/lib/i386-linux-gnu/ for
    i386, with a single padsp command.

diff --git a/configure.ac b/configure.ac
index 7a87432..da0bfcd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -759,6 +759,9 @@ AC_ARG_ENABLE([oss-output],
 AC_ARG_ENABLE([oss-wrapper],
     AS_HELP_STRING([--disable-oss-wrapper],[Disable optional OSS wrapper support]))
 
+AC_ARG_WITH([pulsedsp-location],
+    AS_HELP_STRING([--with-pulsedsp-location],[Specify location where OSS wrapper will be installed]))
+
 AS_IF([test "x$enable_oss_output" != "xno" -o "x$enable_oss_wrapper" != "xno"],
     [AC_CHECK_HEADERS([sys/soundcard.h], HAVE_OSS=1, HAVE_OSS=0)],
     HAVE_OSS=0)
@@ -774,7 +777,12 @@ AS_IF([test "x$enable_oss_wrapper" != "xno"],
     [AS_IF([test "x$HAVE_OSS" = "x1"], HAVE_OSS_WRAPPER=1, HAVE_OSS_WRAPPER=0)],
     HAVE_OSS_WRAPPER=0)
 
+AS_IF([test "x$with_pulsedsp_location" != "x"],
+    [PULSEDSP_LOCATION="$with_pulsedsp_location"],
+    [PULSEDSP_LOCATION="\$(pkglibdir)"])
+
 AC_SUBST(HAVE_OSS_OUTPUT)
+AC_SUBST(PULSEDSP_LOCATION)
 AM_CONDITIONAL([HAVE_OSS_OUTPUT], [test "x$HAVE_OSS_OUTPUT" = "x1"])
 AM_CONDITIONAL([HAVE_OSS_WRAPPER], [test "x$HAVE_OSS_WRAPPER" = "x1"])
 AS_IF([test "x$HAVE_OSS_OUTPUT" = "x1"], AC_DEFINE([HAVE_OSS_OUTPUT], 1, [Have OSS output?]))
diff --git a/src/Makefile.am b/src/Makefile.am
index 498a386..5584728 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -923,7 +923,7 @@ padsplib_LTLIBRARIES = libpulsedsp.la
 bin_SCRIPTS += padsp
 
 edit = @SED@ \
-       -e "s|@pkglibdir[@]|$(pkglibdir)|g"
+       -e 's|@PULSEDSP_LOCATION[@]|$(PULSEDSP_LOCATION)|g'
 
 padsp: utils/padsp.in
 	$(edit) $< > $@
diff --git a/src/utils/padsp.in b/src/utils/padsp.in
index 3430a65..fea00d2 100644
--- a/src/utils/padsp.in
+++ b/src/utils/padsp.in
@@ -74,9 +74,9 @@ done
 shift $(( $OPTIND - 1 ))
 
 if [ x"$LD_PRELOAD" = x ] ; then
-   LD_PRELOAD="@pkglibdir@/libpulsedsp.so"
+   LD_PRELOAD="@PULSEDSP_LOCATION@/libpulsedsp.so"
 else
-   LD_PRELOAD="$LD_PRELOAD @pkglibdir@/libpulsedsp.so"
+   LD_PRELOAD="$LD_PRELOAD @PULSEDSP_LOCATION@/libpulsedsp.so"
 fi
 
 export LD_PRELOAD

commit 4bc753238f5775c23e2abd337e6372cfb7411fb8
Author: Philip Chimento <philip.chimento at gmail.com>
Date:   Sat Nov 12 12:48:44 2016 -0800

    build-sys: Don't override libtoolize
    
    The autoreconf invocation below will already pick up any overrides the
    user might have made to their LIBTOOLIZE variable. Overriding it here
    will break on Darwin systems where libtoolize is not called glibtoolize,
    and is not necessary, so just remove it.

diff --git a/bootstrap.sh b/bootstrap.sh
index 187abaa..57494fc 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -15,13 +15,6 @@
 # You should have received a copy of the GNU Lesser General Public License
 # along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
 
-case $(uname) in
-	*Darwin*)
-		LIBTOOLIZE="glibtoolize"
-		;;
-esac
-test "x$LIBTOOLIZE" = "x" && LIBTOOLIZE=libtoolize
-
 if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then
     cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit && \
     chmod +x .git/hooks/pre-commit && \

commit f790925c7b3767002afa33a0e63fb049c3d93da2
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Thu Nov 10 13:22:04 2016 +0200

    x11-bell: ignore volume from X11
    
    X11 has its own bell volume setting, controlled with the "xset b"
    command. If we use that volume, then the "System Sounds" slider in
    pavucontrol doesn't affect the x11-bell sample volume, which in my
    opinion is a bad thing. Ignoring the volume suggestion from X11 allows
    module-stream-restore to apply the "event" role volume.

diff --git a/src/modules/x11/module-x11-bell.c b/src/modules/x11/module-x11-bell.c
index 6c9f580..7b2be57 100644
--- a/src/modules/x11/module-x11-bell.c
+++ b/src/modules/x11/module-x11-bell.c
@@ -76,7 +76,10 @@ static int x11_event_cb(pa_x11_wrapper *w, XEvent *e, void *userdata) {
 
     bne = (XkbBellNotifyEvent*) e;
 
-    if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, ((pa_volume_t) bne->percent*PA_VOLUME_NORM)/100U, NULL, NULL) < 0) {
+    /* We could use bne->percent to set the volume, but then the "event" role
+     * volume wouldn't have effect. It's better to ignore the volume suggestion
+     * from X11. */
+    if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, PA_VOLUME_INVALID, NULL, NULL) < 0) {
         pa_log_info("Ringing bell failed, reverting to X11 device bell.");
         XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent);
     }

commit 8405b1fb7f03ca11512321df1e9ed8fc806dc662
Author: Philip Chimento <philip.chimento at gmail.com>
Date:   Wed Nov 9 21:22:53 2016 -0800

    build-sys: Move TLS check after flags additions
    
    Any compiler flags should be set before asking the compiler to check for
    thread-local storage with AX_TLS, since compiler flags (in this case
    -mmacosx-version-min=10.5) can influence the outcome of that check.

diff --git a/configure.ac b/configure.ac
index 65a9eba..7a87432 100644
--- a/configure.ac
+++ b/configure.ac
@@ -502,20 +502,6 @@ AC_CACHE_CHECK([whether $CC knows _Bool],
 AS_IF([test "$pulseaudio_cv__Bool" = "yes"], AC_DEFINE([HAVE_STD_BOOL], 1, [Have _Bool.]))
 
 
-#### Thread support ####
-
-AX_TLS
-AS_IF([test "$ac_cv_tls" = "__thread"],
-    AC_DEFINE([SUPPORT_TLS___THREAD], 1, [Define this if the compiler supports __thread for Thread-Local Storage]))
-
-# Win32 build breaks with win32 pthread installed
-AS_IF([test "x$os_is_win32" != "x1"],
-  [AX_PTHREAD])
-
-AS_IF([test "x$ax_pthread_ok" = "xyes"],
-    AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], 1, [Needed on Solaris]))
-
-
 #### Check for libs ####
 
 # ISO
@@ -1405,6 +1391,20 @@ AS_IF([test "x$enable_adrian_aec" != "xno"],
 AM_CONDITIONAL([HAVE_ADRIAN_EC], [test "x$HAVE_ADRIAN_EC" = "x1"])
 
 
+#### Thread support ####
+
+AX_TLS
+AS_IF([test "$ac_cv_tls" = "__thread"],
+    AC_DEFINE([SUPPORT_TLS___THREAD], 1, [Define this if the compiler supports __thread for Thread-Local Storage]))
+
+# Win32 build breaks with win32 pthread installed
+AS_IF([test "x$os_is_win32" != "x1"],
+  [AX_PTHREAD])
+
+AS_IF([test "x$ax_pthread_ok" = "xyes"],
+    AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], 1, [Needed on Solaris]))
+
+
 
 ###################################
 #            Output               #

commit a4bc0227b3fb567234b2251a7ee8189672514cc9
Author: Paul Seyfert <paul.seyfert at mib.infn.it>
Date:   Tue Nov 8 12:28:55 2016 +0100

    shell-completion: update zsh completion for pasuspender
    
     * remove suggestion of '-C' due to incorrect ordering of options for
       _arguments
     * avoid suggesting multiple options
     * add suggestion of "--", followed by executable programs
     * after "--server=<hostname>" or "-s <hostname>" suggest "--"
     * after "-- <program>" continue standard tab completion
    
    BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=98639

diff --git a/shell-completion/zsh/_pulseaudio b/shell-completion/zsh/_pulseaudio
index e065085..0e9e89b 100644
--- a/shell-completion/zsh/_pulseaudio
+++ b/shell-completion/zsh/_pulseaudio
@@ -600,10 +600,26 @@ _pacmd_completion() {
 }
 
 _pasuspender_completion() {
-    _arguments -S -A "-*" -C \
-        {-h,--help}'[display help and exit]' \
-        '--version[show version and exit]' \
-        {-s,--server=}'[name of server to connect to]:host:_hosts' \
+    # parenthesis expressions specify which options should not get suggested afterwards
+    # e.g. after -s, help and version will not get suggested, -- remains the only valid argument
+    # after "--" external commands get suggested
+    _arguments -C \
+        '(-)'{-h,--help}'[display help and exit]' \
+        '(-)--version[show version and exit]' \
+        '(-h --help --version -s --server)'{-s,--server=}'[name of server to connect to]:host:_hosts' \
+        '(-)--[program]:program: _command_names -e' && return 0
+    # check if "--" is one of the words in the command line
+    # if so, ${words[(i)--]} is the position of "--"
+    # shift the arguments and reduce CURRENT, such that further completions see only what follows "--"
+    if [[ ${words[(r)--]} == "--" ]]; then
+        for ((j = ${words[(i)--]}; j > 0; j--));
+        do
+            shift words
+            (( CURRENT-- ))
+        done
+        # proceed with normal completion
+        _normal
+    fi
 }
 
 _padsp_completion() {

commit 3397127f0045d3c130b59e59b4ce6b3601b239b9
Author: Pali Rohár <pali.rohar at gmail.com>
Date:   Sun Sep 11 16:41:02 2016 +0200

    bluetooth: Add optional heuristic for switching between hsp and a2dp profiles
    
    Not all VOIP applications (specially those which use alsa) set media.role to
    phone. This means we need some heuristic to determinate if we want to switch
    from a2dp to hsp profile based on number and types of source output (recording)
    streams.
    
    And also some people want to use their bluetooth headset (with microphone) as
    their default recording device but some do not want to because of low quality.
    
    This patch implements optional heuristic which is disabled by default. It is
    disabled by default to not break experience of current pulseaudio users because
    heuristic cannot be optimal. Heuristic is implemented in module-bluetooth-policy
    module and decide if pulseaudio should switch to a hsp profile or not. It checks
    if there is some source output with pass all these conditions:
    
    * does not have set media.role
    * does not use peak resample method (which is used by desktop volume programs)
    * has assigned client/application (non virtual stream)
    * does not record from monitor of sink
    
    And if yes it switch to hsp profile.
    
    By default this heuristic is disabled and can be enabled when loading module
    module-bluetooth-policy with specifying parameter auto_switch=2
    
    Because it is disabled by default nobody will be affected by this change unless
    manually change auto_switch parameter.
    
    Signed-off-by: Pali Rohár <pali.rohar at gmail.com>

diff --git a/src/modules/bluetooth/module-bluetooth-policy.c b/src/modules/bluetooth/module-bluetooth-policy.c
index df702cc..8d9cd0f 100644
--- a/src/modules/bluetooth/module-bluetooth-policy.c
+++ b/src/modules/bluetooth/module-bluetooth-policy.c
@@ -38,7 +38,7 @@ PA_MODULE_DESCRIPTION("Policy module to make using bluetooth devices out-of-the-
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(true);
 PA_MODULE_USAGE(
-        "auto_switch=<Switch between hsp and a2dp profile?> "
+        "auto_switch=<Switch between hsp and a2dp profile? (0 - never, 1 - media.role=phone, 2 - heuristic> "
         "a2dp_source=<Handle a2dp_source card profile (sink role)?> "
         "ag=<Handle headset_audio_gateway card profile (headset role)?> "
         "hfgw=<Handle hfgw card profile (headset role)?> DEPRECATED");
@@ -52,6 +52,7 @@ static const char* const valid_modargs[] = {
 };
 
 struct userdata {
+    uint32_t auto_switch;
     bool enable_a2dp_source;
     bool enable_ag;
     pa_hook_slot *source_put_slot;
@@ -214,7 +215,8 @@ static void switch_profile(pa_card *card, bool revert_to_a2dp, void *userdata) {
 }
 
 /* Return true if we should ignore this source output */
-static bool ignore_output(pa_source_output *source_output) {
+static bool ignore_output(pa_source_output *source_output, void *userdata) {
+    struct userdata *u = userdata;
     const char *s;
 
     /* New applications could set media.role for identifying streams */
@@ -223,16 +225,32 @@ static bool ignore_output(pa_source_output *source_output) {
     if (s)
         return !pa_streq(s, "phone");
 
-    return true;
+    /* If media.role is not set use some heuristic (if enabled) */
+    if (u->auto_switch != 2)
+        return true;
+
+    /* Ignore if resample method is peaks (used by desktop volume programs) */
+    if (pa_source_output_get_resample_method(source_output) == PA_RESAMPLER_PEAKS)
+        return true;
+
+    /* Ignore if there is no client/application assigned (used by virtual stream) */
+    if (!source_output->client)
+        return true;
+
+    /* Ignore if recording from monitor of sink */
+    if (source_output->direct_on_input)
+        return true;
+
+    return false;
 }
 
-static unsigned source_output_count(pa_core *c) {
+static unsigned source_output_count(pa_core *c, void *userdata) {
     pa_source_output *source_output;
     uint32_t idx;
     unsigned count = 0;
 
     PA_IDXSET_FOREACH(source_output, c->source_outputs, idx)
-        if (!ignore_output(source_output))
+        if (!ignore_output(source_output, userdata))
             ++count;
 
     return count;
@@ -252,7 +270,7 @@ static pa_hook_result_t source_output_put_hook_callback(pa_core *c, pa_source_ou
     pa_assert(c);
     pa_assert(source_output);
 
-    if (ignore_output(source_output))
+    if (ignore_output(source_output, userdata))
         return PA_HOOK_OK;
 
     switch_profile_all(c->cards, false, userdata);
@@ -264,11 +282,11 @@ static pa_hook_result_t source_output_unlink_hook_callback(pa_core *c, pa_source
     pa_assert(c);
     pa_assert(source_output);
 
-    if (ignore_output(source_output))
+    if (ignore_output(source_output, userdata))
         return PA_HOOK_OK;
 
     /* If there are still some source outputs do nothing. */
-    if (source_output_count(c) > 0)
+    if (source_output_count(c, userdata) > 0)
         return PA_HOOK_OK;
 
     switch_profile_all(c->cards, true, userdata);
@@ -282,7 +300,7 @@ static pa_hook_result_t card_init_profile_hook_callback(pa_core *c, pa_card *car
     pa_assert(c);
     pa_assert(card);
 
-    if (source_output_count(c) == 0)
+    if (source_output_count(c, userdata) == 0)
         return PA_HOOK_OK;
 
     /* Only consider bluetooth cards */
@@ -394,7 +412,6 @@ static void handle_all_profiles(pa_core *core) {
 int pa__init(pa_module *m) {
     pa_modargs *ma;
     struct userdata *u;
-    bool auto_switch;
 
     pa_assert(m);
 
@@ -405,8 +422,8 @@ int pa__init(pa_module *m) {
 
     m->userdata = u = pa_xnew0(struct userdata, 1);
 
-    auto_switch = true;
-    if (pa_modargs_get_value_boolean(ma, "auto_switch", &auto_switch) < 0) {
+    u->auto_switch = 1;
+    if (pa_modargs_get_value_u32(ma, "auto_switch", &u->auto_switch) < 0) {
         pa_log("Failed to parse auto_switch argument.");
         goto fail;
     }
@@ -435,7 +452,7 @@ int pa__init(pa_module *m) {
     u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL,
                                        (pa_hook_cb_t) sink_put_hook_callback, u);
 
-    if (auto_switch) {
+    if (u->auto_switch) {
         u->source_output_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_NORMAL,
                                                     (pa_hook_cb_t) source_output_put_hook_callback, u);
 

commit 19873dff0acf9e204066458e7ab465fba759142d
Author: Hajime Fujita <crisp.fujita at nifty.com>
Date:   Sun Jan 31 22:16:05 2016 -0600

    rtp: Introduce pa_rtsp_exec_ready()
    
    In the current RTSP implementation, there is a vulnerable window
    between the RTSP object creation and the URL initialization.
    If any RTSP command is issued during this period, it will lead to
    crash by assertion violation.
    
    This patch introduces pa_rtsp_exec_ready(), which returns if it is
    safe to issue RTSP commands.
    
    Reviewed-by: Anton Lundin <glance at acc.umu.se>

diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index 6e65860..198417e 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -389,6 +389,12 @@ void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key) {
     pa_headerlist_remove(c->headers, key);
 }
 
+bool pa_rtsp_exec_ready(const pa_rtsp_client *c) {
+    pa_assert(c);
+
+    return c->url != NULL && c->ioline != NULL;
+}
+
 static int rtsp_exec(pa_rtsp_client *c, const char *cmd,
                         const char *content_type, const char *content,
                         int expect_response,
diff --git a/src/modules/rtp/rtsp_client.h b/src/modules/rtp/rtsp_client.h
index 7e9978d..abc60ee 100644
--- a/src/modules/rtp/rtsp_client.h
+++ b/src/modules/rtp/rtsp_client.h
@@ -58,6 +58,8 @@ void pa_rtsp_set_url(pa_rtsp_client *c, const char *url);
 void pa_rtsp_add_header(pa_rtsp_client *c, const char *key, const char *value);
 void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key);
 
+bool pa_rtsp_exec_ready(const pa_rtsp_client *c);
+
 int pa_rtsp_options(pa_rtsp_client *c);
 int pa_rtsp_announce(pa_rtsp_client *c, const char *sdp);
 

commit 60a1c275170a250e1c128024e6668481047e5c64
Author: Martin Blanchard <tinram at gmx.fr>
Date:   Sun Jan 31 22:16:04 2016 -0600

    rtp: Random seq number at the beginning of the session
    
    Reviewed-by: Anton Lundin <glance at acc.umu.se>

diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index aa0217d..6e65860 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -41,6 +41,7 @@
 #include <pulsecore/strbuf.h>
 #include <pulsecore/ioline.h>
 #include <pulsecore/arpa-inet.h>
+#include <pulsecore/random.h>
 
 #include "rtsp_client.h"
 
@@ -199,7 +200,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
     }
     if (c->waiting) {
         pa_log_warn("Unexpected response: %s", s2);
-        goto exit;;
+        goto exit;
     }
     if (!strlen(s2)) {
         /* End of headers */
@@ -504,8 +505,8 @@ int pa_rtsp_record(pa_rtsp_client *c, uint16_t *seq, uint32_t *rtptime) {
         return -1;
     }
 
-    /* Todo: Generate these values randomly as per spec */
-    *seq = *rtptime = 0;
+    pa_random(seq, sizeof(*seq));
+    pa_random(rtptime, sizeof(*rtptime));
 
     headers = pa_headerlist_new();
     pa_headerlist_puts(headers, "Range", "npt=0-");

commit a0199dd99d81d793ac4572be41608835d8be35ab
Author: Colin Leroy <colin at colino.net>
Date:   Sun Jan 31 22:16:03 2016 -0600

    rtp: New pa_rtsp_options function
    
    Add a function performing a call to the OPTIONS request; also,
    in some special cases, tuning transport parameters is required (default:
    "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record") ! The RAOP client for
    example needs to overwrite them.
    
    Reviewed-by: Anton Lundin <glance at acc.umu.se>

diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
index ef5c967..31e0fdc 100644
--- a/src/modules/raop/raop_client.c
+++ b/src/modules/raop/raop_client.c
@@ -299,10 +299,14 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist *he
             break;
         }
 
+        case STATE_OPTIONS:
+            pa_log_debug("RAOP: OPTIONS");
+            break;
+
         case STATE_ANNOUNCE:
             pa_log_debug("RAOP: ANNOUNCED");
             pa_rtsp_remove_header(c->rtsp, "Apple-Challenge");
-            pa_rtsp_setup(c->rtsp);
+            pa_rtsp_setup(c->rtsp, NULL);
             break;
 
         case STATE_SETUP: {
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index 56f6e15..aa0217d 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -443,6 +443,22 @@ static int rtsp_exec(pa_rtsp_client *c, const char *cmd,
     return 0;
 }
 
+int pa_rtsp_options(pa_rtsp_client *c) {
+    char *url;
+    int rv;
+
+    pa_assert(c);
+
+    url = c->url;
+    c->state = STATE_OPTIONS;
+
+    c->url = (char *)"*";
+    rv = rtsp_exec(c, "OPTIONS", NULL, NULL, 0, NULL);
+
+    c->url = url;
+    return rv;
+}
+
 int pa_rtsp_announce(pa_rtsp_client *c, const char *sdp) {
     int rv;
 
@@ -457,14 +473,17 @@ int pa_rtsp_announce(pa_rtsp_client *c, const char *sdp) {
     return rv;
 }
 
-int pa_rtsp_setup(pa_rtsp_client *c) {
+int pa_rtsp_setup(pa_rtsp_client *c, const char *transport) {
     pa_headerlist *headers;
     int rv;
 
     pa_assert(c);
 
     headers = pa_headerlist_new();
-    pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
+    if (!transport)
+        pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
+    else
+        pa_headerlist_puts(headers, "Transport", transport);
 
     c->state = STATE_SETUP;
     rv = rtsp_exec(c, "SETUP", NULL, NULL, 1, headers);
diff --git a/src/modules/rtp/rtsp_client.h b/src/modules/rtp/rtsp_client.h
index d51acda..7e9978d 100644
--- a/src/modules/rtp/rtsp_client.h
+++ b/src/modules/rtp/rtsp_client.h
@@ -33,6 +33,7 @@
 typedef struct pa_rtsp_client pa_rtsp_client;
 typedef enum {
   STATE_CONNECT,
+  STATE_OPTIONS,
   STATE_ANNOUNCE,
   STATE_SETUP,
   STATE_RECORD,
@@ -57,9 +58,10 @@ void pa_rtsp_set_url(pa_rtsp_client *c, const char *url);
 void pa_rtsp_add_header(pa_rtsp_client *c, const char *key, const char *value);
 void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key);
 
+int pa_rtsp_options(pa_rtsp_client *c);
 int pa_rtsp_announce(pa_rtsp_client *c, const char *sdp);
 
-int pa_rtsp_setup(pa_rtsp_client *c);
+int pa_rtsp_setup(pa_rtsp_client *c, const char *transport);
 int pa_rtsp_record(pa_rtsp_client *c, uint16_t *seq, uint32_t *rtptime);
 int pa_rtsp_teardown(pa_rtsp_client *c);
 

commit 736fabf0ca457818ae179ee81cf781d5120471cc
Author: Martin Blanchard <tinram at gmx.fr>
Date:   Sun Jan 31 22:16:01 2016 -0600

    raop: Add pulsecore/core-utils a pa_str_in_list function
    
    Reviewed-by: Anton Lundin <glance at acc.umu.se>

diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 873f86a..f2999b2 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -2981,6 +2981,26 @@ bool pa_in_system_mode(void) {
     return !!atoi(e);
 }
 
+/* Checks a delimiters-separated list of words in haystack for needle */
+bool pa_str_in_list(const char *haystack, const char *delimiters, const char *needle) {
+    char *s;
+    const char *state = NULL;
+
+    if (!haystack || !needle)
+        return false;
+
+    while ((s = pa_split(haystack, delimiters, &state))) {
+        if (pa_streq(needle, s)) {
+            pa_xfree(s);
+            return true;
+        }
+
+        pa_xfree(s);
+    }
+
+    return false;
+}
+
 /* Checks a whitespace-separated list of words in haystack for needle */
 bool pa_str_in_list_spaces(const char *haystack, const char *needle) {
     char *s;
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index ede1ae2..8847528 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -109,8 +109,8 @@ static inline const char *pa_strna(const char *x) {
     return x ? x : "n/a";
 }
 
-char *pa_split(const char *c, const char*delimiters, const char **state);
-const char *pa_split_in_place(const char *c, const char*delimiters, int *n, const char **state);
+char *pa_split(const char *c, const char *delimiters, const char **state);
+const char *pa_split_in_place(const char *c, const char *delimiters, int *n, const char **state);
 char *pa_split_spaces(const char *c, const char **state);
 
 char *pa_strip_nl(char *s);
@@ -228,6 +228,7 @@ static inline bool pa_safe_streq(const char *a, const char *b) {
 }
 
 bool pa_str_in_list_spaces(const char *needle, const char *haystack);
+bool pa_str_in_list(const char *haystack, const char *delimiters, const char *needle);
 
 char *pa_get_host_name_malloc(void);
 char *pa_get_user_name_malloc(void);

commit 6665acac56a348e880a7345906604d30f2323d37
Author: Martin Blanchard <tinram at gmx.fr>
Date:   Sun Jan 31 22:16:00 2016 -0600

    raop: Cosmetic fixes / Match coding style
    
    Reviewed-by: Anton Lundin <glance at acc.umu.se>

diff --git a/src/modules/raop/base64.c b/src/modules/raop/base64.c
index 1e09740..073e6a2 100644
--- a/src/modules/raop/base64.c
+++ b/src/modules/raop/base64.c
@@ -17,10 +17,10 @@
   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
 ***/
 
-/*
+/***
   This file was originally inspired by a file developed by
   Kungliga Tekniska högskolan
-*/
+***/
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
@@ -37,11 +37,17 @@ static const char base64_chars[] =
     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 static int pos(char c) {
-    if (c >= 'A' && c <= 'Z') return c - 'A' + 0;
-    if (c >= 'a' && c <= 'z') return c - 'a' + 26;
-    if (c >= '0' && c <= '9') return c - '0' + 52;
-    if (c == '+') return 62;
-    if (c == '/') return 63;
+    if (c >= 'A' && c <= 'Z')
+        return c - 'A' + 0;
+    if (c >= 'a' && c <= 'z')
+        return c - 'a' + 26;
+    if (c >= '0' && c <= '9')
+        return c - '0' + 52;
+    if (c == '+')
+        return 62;
+    if (c == '/')
+        return 63;
+
     return -1;
 }
 
@@ -73,8 +79,10 @@ int pa_base64_encode(const void *data, int size, char **str) {
             p[2] = '=';
         p += 4;
     }
+
     *p = 0;
     *str = s;
+
     return strlen(s);
 }
 
@@ -84,6 +92,7 @@ static unsigned int token_decode(const char *token) {
     int i;
     unsigned int val = 0;
     int marker = 0;
+
     if (strlen(token) < 4)
         return DECODE_ERROR;
     for (i = 0; i < 4; i++) {
@@ -99,8 +108,10 @@ static unsigned int token_decode(const char *token) {
             val += lpos;
         }
     }
+
     if (marker > 2)
         return DECODE_ERROR;
+
     return (marker << 24) | val;
 }
 
@@ -120,5 +131,6 @@ int pa_base64_decode(const char *str, void *data) {
         if (marker < 1)
             *q++ = val & 0xff;
     }
+
     return q - (unsigned char *) data;
 }
diff --git a/src/modules/raop/base64.h b/src/modules/raop/base64.h
index 59160c9..478fa4d 100644
--- a/src/modules/raop/base64.h
+++ b/src/modules/raop/base64.h
@@ -21,10 +21,10 @@
   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
 ***/
 
-/*
+/***
   This file was originally inspired by a file developed by
   Kungliga Tekniska högskolan
-*/
+***/
 
 int pa_base64_encode(const void *data, int size, char **str);
 int pa_base64_decode(const char *str, void *data);
diff --git a/src/modules/raop/module-raop-discover.c b/src/modules/raop/module-raop-discover.c
index f083044..1ced777 100644
--- a/src/modules/raop/module-raop-discover.c
+++ b/src/modules/raop/module-raop-discover.c
@@ -52,6 +52,17 @@ PA_MODULE_LOAD_ONCE(true);
 
 #define SERVICE_TYPE_SINK "_raop._tcp"
 
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    AvahiPoll *avahi_poll;
+    AvahiClient *client;
+    AvahiServiceBrowser *sink_browser;
+
+    pa_hashmap *tunnels;
+};
+
 static const char* const valid_modargs[] = {
     NULL
 };
@@ -63,16 +74,6 @@ struct tunnel {
     uint32_t module_index;
 };
 
-struct userdata {
-    pa_core *core;
-    pa_module *module;
-    AvahiPoll *avahi_poll;
-    AvahiClient *client;
-    AvahiServiceBrowser *sink_browser;
-
-    pa_hashmap *tunnels;
-};
-
 static unsigned tunnel_hash(const void *p) {
     const struct tunnel *t = p;
 
@@ -102,11 +103,11 @@ static int tunnel_compare(const void *a, const void *b) {
     return 0;
 }
 
-static struct tunnel *tunnel_new(
+static struct tunnel* tunnel_new(
         AvahiIfIndex interface, AvahiProtocol protocol,
         const char *name, const char *type, const char *domain) {
-
     struct tunnel *t;
+
     t = pa_xnew(struct tunnel, 1);
     t->interface = interface;
     t->protocol = protocol;
@@ -114,6 +115,7 @@ static struct tunnel *tunnel_new(
     t->type = pa_xstrdup(type);
     t->domain = pa_xstrdup(domain);
     t->module_index = PA_IDXSET_INVALID;
+
     return t;
 }
 
@@ -134,88 +136,87 @@ static void resolver_cb(
         AvahiStringList *txt,
         AvahiLookupResultFlags flags,
         void *userdata) {
-
     struct userdata *u = userdata;
     struct tunnel *tnl;
+    char *device = NULL, *nicename, *dname, *vname, *args;
+    char at[AVAHI_ADDRESS_STR_MAX];
+    AvahiStringList *l;
+    pa_module *m;
 
     pa_assert(u);
 
     tnl = tunnel_new(interface, protocol, name, type, domain);
 
-    if (event != AVAHI_RESOLVER_FOUND)
+    if (event != AVAHI_RESOLVER_FOUND) {
         pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client)));
-    else {
-        char *device = NULL, *nicename, *dname, *vname, *args;
-        char at[AVAHI_ADDRESS_STR_MAX];
-        AvahiStringList *l;
-        pa_module *m;
-
-        if ((nicename = strstr(name, "@"))) {
-            ++nicename;
-            if (strlen(nicename) > 0) {
-                pa_log_debug("Found RAOP: %s", nicename);
-                nicename = pa_escape(nicename, "\"'");
-            } else
-                nicename = NULL;
-        }
+        goto  finish;
+    }
 
-        for (l = txt; l; l = l->next) {
-            char *key, *value;
-            pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0);
+    if ((nicename = strstr(name, "@"))) {
+        ++nicename;
+        if (strlen(nicename) > 0) {
+            pa_log_debug("Found RAOP: %s", nicename);
+            nicename = pa_escape(nicename, "\"'");
+        } else
+            nicename = NULL;
+    }
 
-            pa_log_debug("Found key: '%s' with value: '%s'", key, value);
-            if (pa_streq(key, "device")) {
-                pa_xfree(device);
-                device = value;
-                value = NULL;
-            }
-            avahi_free(key);
-            avahi_free(value);
+    for (l = txt; l; l = l->next) {
+        char *key, *value;
+        pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0);
+
+        pa_log_debug("Found key: '%s' with value: '%s'", key, value);
+        if (pa_streq(key, "device")) {
+            device = value;
+            value = NULL;
         }
 
-        if (device)
-            dname = pa_sprintf_malloc("raop.%s.%s", host_name, device);
-        else
-            dname = pa_sprintf_malloc("raop.%s", host_name);
+        avahi_free(key);
+        avahi_free(value);
+    }
 
-        if (!(vname = pa_namereg_make_valid_name(dname))) {
-            pa_log("Cannot construct valid device name from '%s'.", dname);
-            avahi_free(device);
-            pa_xfree(dname);
-            goto finish;
-        }
+    if (device)
+        dname = pa_sprintf_malloc("raop.%s.%s", host_name, device);
+    else
+        dname = pa_sprintf_malloc("raop.%s", host_name);
+
+    if (!(vname = pa_namereg_make_valid_name(dname))) {
+        pa_log("Cannot construct valid device name from '%s'.", dname);
+        avahi_free(device);
         pa_xfree(dname);
+        goto finish;
+    }
 
-        if (nicename) {
-            args = pa_sprintf_malloc("server=[%s]:%u "
-                                     "sink_name=%s "
-                                     "sink_properties='device.description=\"%s\"'",
-                                     avahi_address_snprint(at, sizeof(at), a), port,
-                                     vname,
-                                     nicename);
-            pa_xfree(nicename);
-        } else {
-            args = pa_sprintf_malloc("server=[%s]:%u "
-                                     "sink_name=%s",
-                                     avahi_address_snprint(at, sizeof(at), a), port,
-                                     vname);
-        }
+    avahi_free(device);
+    pa_xfree(dname);
+
+    if (nicename) {
+        args = pa_sprintf_malloc("server=[%s]:%u "
+                                 "sink_name=%s "
+                                 "sink_properties='device.description=\"%s\"'",
+                                 avahi_address_snprint(at, sizeof(at), a), port,
+                                 vname,
+                                 nicename);
+        pa_xfree(nicename);
+    } else {
+        args = pa_sprintf_malloc("server=[%s]:%u "
+                                 "sink_name=%s",
+                                 avahi_address_snprint(at, sizeof(at), a), port,
+                                 vname);
+    }
 
-        pa_log_debug("Loading module-raop-sink with arguments '%s'", args);
+    pa_log_debug("Loading module-raop-sink with arguments '%s'", args);
 
-        if ((m = pa_module_load(u->core, "module-raop-sink", args))) {
-            tnl->module_index = m->index;
-            pa_hashmap_put(u->tunnels, tnl, tnl);
-            tnl = NULL;
-        }
-
-        pa_xfree(vname);
-        pa_xfree(args);
-        avahi_free(device);
+    if ((m = pa_module_load(u->core, "module-raop-sink", args))) {
+        tnl->module_index = m->index;
+        pa_hashmap_put(u->tunnels, tnl, tnl);
+        tnl = NULL;
     }
 
-finish:
+    pa_xfree(vname);
+    pa_xfree(args);
 
+finish:
     avahi_service_resolver_free(r);
 
     if (tnl)
@@ -229,7 +230,6 @@ static void browser_cb(
         const char *name, const char *type, const char *domain,
         AvahiLookupResultFlags flags,
         void *userdata) {
-
     struct userdata *u = userdata;
     struct tunnel *t;
 
@@ -248,7 +248,7 @@ static void browser_cb(
 
         /* We ignore the returned resolver object here, since the we don't
          * need to attach any special data to it, and we can still destroy
-         * it from the callback */
+         * it from the callback. */
 
     } else if (event == AVAHI_BROWSER_REMOVE) {
         struct tunnel *t2;
@@ -275,9 +275,7 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
         case AVAHI_CLIENT_S_REGISTERING:
         case AVAHI_CLIENT_S_RUNNING:
         case AVAHI_CLIENT_S_COLLISION:
-
             if (!u->sink_browser) {
-
                 if (!(u->sink_browser = avahi_service_browser_new(
                               c,
                               AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
@@ -299,16 +297,16 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
 
                 pa_log_debug("Avahi daemon disconnected.");
 
+                /* Try to reconnect. */
                 if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
                     pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
                     pa_module_unload_request(u->module, true);
                 }
             }
 
-            /* Fall through */
+            /* Fall through. */
 
         case AVAHI_CLIENT_CONNECTING:
-
             if (u->sink_browser) {
                 avahi_service_browser_free(u->sink_browser);
                 u->sink_browser = NULL;
@@ -316,12 +314,12 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
 
             break;
 
-        default: ;
+        default:
+            break;
     }
 }
 
-int pa__init(pa_module*m) {
-
+int pa__init(pa_module *m) {
     struct userdata *u;
     pa_modargs *ma = NULL;
     int error;
@@ -358,8 +356,9 @@ fail:
     return -1;
 }
 
-void pa__done(pa_module*m) {
-    struct userdata*u;
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
     pa_assert(m);
 
     if (!(u = m->userdata))
diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c
index 7a97e83..c49ad52 100644
--- a/src/modules/raop/module-raop-sink.c
+++ b/src/modules/raop/module-raop-sink.c
@@ -124,10 +124,10 @@ enum {
     SINK_MESSAGE_RIP_SOCKET
 };
 
-/* Forward declaration */
+/* Forward declarations: */
 static void sink_set_volume_cb(pa_sink *);
 
-static void on_connection(int fd, void*userdata) {
+static void on_connection(int fd, void *userdata) {
     int so_sndbuf = 0;
     socklen_t sl = sizeof(int);
     struct userdata *u = userdata;
@@ -143,7 +143,7 @@ static void on_connection(int fd, void*userdata) {
         pa_sink_set_max_request(u->sink, PA_MAX((size_t) so_sndbuf, u->block_size));
     }
 
-    /* Set the initial volume */
+    /* Set the initial volume. */
     sink_set_volume_cb(u->sink);
 
     pa_log_debug("Connection authenticated, handing fd to IO thread...");
@@ -174,7 +174,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
                     pa_smoother_pause(u->smoother, pa_rtclock_now());
 
-                    /* Issue a FLUSH if we are connected */
+                    /* Issue a FLUSH if we are connected. */
                     if (u->fd >= 0) {
                         pa_raop_flush(u->raop);
                     }
@@ -187,7 +187,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
                         pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
 
                         /* The connection can be closed when idle, so check to
-                           see if we need to reestablish it */
+                         * see if we need to reestablish it. */
                         if (u->fd < 0)
                             pa_raop_connect(u->raop);
                         else
@@ -226,7 +226,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             /*pollfd->events = */pollfd->revents = 0;
 
             if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
-                /* Our stream has been suspended so we just flush it.... */
+                /* Our stream has been suspended so we just flush it... */
                 pa_raop_flush(u->raop);
             }
             return 0;
@@ -268,19 +268,19 @@ static void sink_set_volume_cb(pa_sink *s) {
 
     pa_assert(u);
 
-    /* If we're muted we don't need to do anything */
+    /* If we're muted we don't need to do anything. */
     if (s->muted)
         return;
 
     /* Calculate the max volume of all channels.
-       We'll use this as our (single) volume on the APEX device and emulate
-       any variation in channel volumes in software */
+     * We'll use this as our (single) volume on the APEX device and emulate
+     * any variation in channel volumes in software. */
     v = pa_cvolume_max(&s->real_volume);
 
-    /* Create a pa_cvolume version of our single value */
+    /* Create a pa_cvolume version of our single value. */
     pa_cvolume_set(&hw, s->sample_spec.channels, v);
 
-    /* Perform any software manipulation of the volume needed */
+    /* Perform any software manipulation of the volume needed. */
     pa_sw_cvolume_divide(&s->soft_volume, &s->real_volume, &hw);
 
     pa_log_debug("Requested volume: %s", pa_cvolume_snprint_verbose(t, sizeof(t), &s->real_volume, &s->channel_map, false));
@@ -289,7 +289,7 @@ static void sink_set_volume_cb(pa_sink *s) {
                  pa_cvolume_snprint_verbose(t, sizeof(t), &s->soft_volume, &s->channel_map, true));
 
     /* Any necessary software volume manipulation is done so set
-       our hw volume (or v as a single value) on the device */
+     * our hw volume (or v as a single value) on the device. */
     pa_raop_client_set_volume(u->raop, v);
 }
 
@@ -333,7 +333,7 @@ static void thread_func(void *userdata) {
             struct pollfd *pollfd;
             pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
 
-            /* Render some data and write it to the fifo */
+            /* Render some data and write it to the fifo. */
             if (/*PA_SINK_IS_OPENED(u->sink->thread_info.state) && */pollfd->revents) {
                 pa_usec_t usec;
                 int64_t n;
@@ -364,29 +364,29 @@ static void thread_func(void *userdata) {
                         if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
                             size_t rl;
 
-                            /* We render real data */
+                            /* We render real data. */
                             if (u->raw_memchunk.length <= 0) {
                                 if (u->raw_memchunk.memblock)
                                     pa_memblock_unref(u->raw_memchunk.memblock);
                                 pa_memchunk_reset(&u->raw_memchunk);
 
-                                /* Grab unencoded data */
+                                /* Grab unencoded data. */
                                 pa_sink_render(u->sink, u->block_size, &u->raw_memchunk);
                             }
                             pa_assert(u->raw_memchunk.length > 0);
 
-                            /* Encode it */
+                            /* Encode it. */
                             rl = u->raw_memchunk.length;
                             u->encoding_overhead += u->next_encoding_overhead;
                             pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk);
                             u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length));
                             u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length);
                         } else {
-                            /* We render some silence into our memchunk */
+                            /* We render some silence into our memchunk. */
                             memcpy(&u->encoded_memchunk, &silence, sizeof(pa_memchunk));
                             pa_memblock_ref(silence.memblock);
 
-                            /* Calculate/store some values to be used with the smoother */
+                            /* Calculate/store some values to be used with the smoother. */
                             u->next_encoding_overhead = silence_overhead;
                             u->encoding_ratio = silence_ratio;
                         }
@@ -405,8 +405,7 @@ static void thread_func(void *userdata) {
                             continue;
                         else if (errno == EAGAIN) {
 
-                            /* OK, we filled all socket buffers up
-                             * now. */
+                            /* OK, we filled all socket buffers up now. */
                             goto filled_up;
 
                         } else {
@@ -423,22 +422,21 @@ static void thread_func(void *userdata) {
                         pollfd->revents = 0;
 
                         if (u->encoded_memchunk.length > 0) {
-                            /* we've completely written the encoded data, so update our overhead */
+                            /* We've completely written the encoded data, so update our overhead. */
                             u->encoding_overhead += u->next_encoding_overhead;
 
                             /* OK, we wrote less that we asked for,
                              * hence we can assume that the socket
-                             * buffers are full now */
+                             * buffers are full now. */
                             goto filled_up;
                         }
                     }
                 }
 
             filled_up:
-
                 /* At this spot we know that the socket buffers are
                  * fully filled up. This is the best time to estimate
-                 * the playback position of the server */
+                 * the playback position of the server. */
 
                 n = u->offset - u->encoding_overhead;
 
@@ -460,7 +458,7 @@ static void thread_func(void *userdata) {
                 pa_smoother_put(u->smoother, pa_rtclock_now(), usec);
             }
 
-            /* Hmm, nothing to do. Let's sleep */
+            /* Hmm, nothing to do. Let's sleep... */
             pollfd->events = POLLOUT; /*PA_SINK_IS_OPENED(u->sink->thread_info.state)  ? POLLOUT : 0;*/
         }
 
@@ -482,7 +480,7 @@ static void thread_func(void *userdata) {
                 }
 
                 /* We expect this to happen on occasion if we are not sending data.
-                   It's perfectly natural and normal and natural */
+                 * It's perfectly natural and normal and natural. */
                 if (u->rtpoll_item)
                     pa_rtpoll_item_free(u->rtpoll_item);
                 u->rtpoll_item = NULL;
@@ -492,7 +490,7 @@ static void thread_func(void *userdata) {
 
 fail:
     /* If this was no regular exit from the loop we have to continue
-     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+     * processing messages until we received PA_MESSAGE_SHUTDOWN. */
     pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
     pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
 
@@ -502,7 +500,7 @@ finish:
     pa_log_debug("Thread shutting down");
 }
 
-int pa__init(pa_module*m) {
+int pa__init(pa_module *m) {
     struct userdata *u = NULL;
     pa_sample_spec ss;
     pa_modargs *ma = NULL;
@@ -645,7 +643,7 @@ int pa__get_n_used(pa_module *m) {
     return pa_sink_linked_by(u->sink);
 }
 
-void pa__done(pa_module*m) {
+void pa__done(pa_module *m) {
     struct userdata *u;
     pa_assert(m);
 
diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
index 864c558..ef5c967 100644
--- a/src/modules/raop/raop_client.c
+++ b/src/modules/raop/raop_client.c
@@ -95,9 +95,9 @@ struct pa_raop_client {
 
     /* Encryption Related bits */
     AES_KEY aes;
-    uint8_t aes_iv[AES_CHUNKSIZE]; /* initialization vector for aes-cbc */
-    uint8_t aes_nv[AES_CHUNKSIZE]; /* next vector for aes-cbc */
-    uint8_t aes_key[AES_CHUNKSIZE]; /* key for aes-cbc */
+    uint8_t aes_iv[AES_CHUNKSIZE]; /* Initialization vector for aes-cbc */
+    uint8_t aes_nv[AES_CHUNKSIZE]; /* Next vector for aes-cbc */
+    uint8_t aes_key[AES_CHUNKSIZE]; /* Key for aes-cbc */
 
     pa_socket_client *sc;
     int fd;
@@ -106,9 +106,9 @@ struct pa_raop_client {
     uint32_t rtptime;
 
     pa_raop_client_cb_t callback;
-    void* userdata;
+    void *userdata;
     pa_raop_client_closed_cb_t closed_callback;
-    void* closed_userdata;
+    void *closed_userdata;
 };
 
 /**
@@ -130,32 +130,32 @@ static inline void bit_writer(uint8_t **buffer, uint8_t *bit_pos, int *size, uin
     if (!*bit_pos)
         *size += 1;
 
-    /* Calc the number of bits left in the current byte of buffer */
+    /* Calc the number of bits left in the current byte of buffer. */
     bits_left = 7 - *bit_pos  + 1;
     /* Calc the overflow of bits in relation to how much space we have left... */
     bit_overflow = bits_left - data_bit_len;
     if (bit_overflow >= 0) {
-        /* We can fit the new data in our current byte */
-        /* As we write from MSB->LSB we need to left shift by the overflow amount */
+        /* We can fit the new data in our current byte.
+         * As we write from MSB->LSB we need to left shift by the overflow amount. */
         bit_data = data << bit_overflow;
         if (*bit_pos)
             **buffer |= bit_data;
         else
             **buffer = bit_data;
-        /* If our data fits exactly into the current byte, we need to increment our pointer */
+        /* If our data fits exactly into the current byte, we need to increment our pointer. */
         if (0 == bit_overflow) {
-            /* Do not increment size as it will be incremented on next call as bit_pos is zero */
+            /* Do not increment size as it will be incremented on next call as bit_pos is zero. */
             *buffer += 1;
             *bit_pos = 0;
         } else {
             *bit_pos += data_bit_len;
         }
     } else {
-        /* bit_overflow is negative, there for we will need a new byte from our buffer */
-        /* Firstly fill up what's left in the current byte */
+        /* bit_overflow is negative, there for we will need a new byte from our buffer
+         * Firstly fill up what's left in the current byte. */
         bit_data = data >> -bit_overflow;
         **buffer |= bit_data;
-        /* Increment our buffer pointer and size counter*/
+        /* Increment our buffer pointer and size counter. */
         *buffer += 1;
         *size += 1;
         **buffer = data << (8 + bit_overflow);
@@ -191,7 +191,7 @@ static int rsa_encrypt(uint8_t *text, int len, uint8_t *res) {
     return size;
 }
 
-static int aes_encrypt(pa_raop_client* c, uint8_t *data, int size) {
+static int aes_encrypt(pa_raop_client *c, uint8_t *data, int size) {
     uint8_t *buf;
     int i=0, j;
 
@@ -246,8 +246,8 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
     c->callback(c->fd, c->userdata);
 }
 
-static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *userdata) {
-    pa_raop_client* c = userdata;
+static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist *headers, void *userdata) {
+    pa_raop_client *c = userdata;
     pa_assert(c);
     pa_assert(rtsp);
     pa_assert(rtsp == c->rtsp);
@@ -263,12 +263,12 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* he
 
             pa_log_debug("RAOP: CONNECTED");
             ip = pa_rtsp_localip(c->rtsp);
-            /* First of all set the url properly */
+            /* First of all set the url properly. */
             url = pa_sprintf_malloc("rtsp://%s/%s", ip, c->sid);
             pa_rtsp_set_url(c->rtsp, url);
             pa_xfree(url);
 
-            /* Now encrypt our aes_public key to send to the device */
+            /* Now encrypt our aes_public key to send to the device. */
             i = rsa_encrypt(c->aes_key, AES_CHUNKSIZE, rsakey);
             pa_base64_encode(rsakey, i, &key);
             rtrimchar(key, '=');
@@ -367,7 +367,7 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* he
             pa_rtsp_client_free(c->rtsp);
             c->rtsp = NULL;
             if (c->fd > 0) {
-                /* We do not close the fd, we leave it to the closed callback to do that */
+                /* We do not close the fd, we leave it to the closed callback to do that. */
                 c->fd = -1;
             }
             if (c->sc) {
@@ -381,7 +381,7 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* he
     }
 }
 
-pa_raop_client* pa_raop_client_new(pa_core *core, const char* host) {
+pa_raop_client* pa_raop_client_new(pa_core *core, const char *host) {
     pa_parsed_address a;
     pa_raop_client* c;
 
@@ -410,10 +410,11 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char* host) {
         pa_raop_client_free(c);
         return NULL;
     }
+
     return c;
 }
 
-void pa_raop_client_free(pa_raop_client* c) {
+void pa_raop_client_free(pa_raop_client *c) {
     pa_assert(c);
 
     if (c->rtsp)
@@ -424,7 +425,7 @@ void pa_raop_client_free(pa_raop_client* c) {
     pa_xfree(c);
 }
 
-int pa_raop_connect(pa_raop_client* c) {
+int pa_raop_connect(pa_raop_client *c) {
     char *sci;
     struct {
         uint32_t a;
@@ -441,30 +442,31 @@ int pa_raop_connect(pa_raop_client* c) {
 
     c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, "iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
 
-    /* Initialise the AES encryption system */
+    /* Initialise the AES encryption system. */
     pa_random(c->aes_iv, sizeof(c->aes_iv));
     pa_random(c->aes_key, sizeof(c->aes_key));
     memcpy(c->aes_nv, c->aes_iv, sizeof(c->aes_nv));
     AES_set_encrypt_key(c->aes_key, 128, &c->aes);
 
-    /* Generate random instance id */
+    /* Generate random instance id. */
     pa_random(&rand_data, sizeof(rand_data));
     c->sid = pa_sprintf_malloc("%u", rand_data.a);
     sci = pa_sprintf_malloc("%08x%08x",rand_data.b, rand_data.c);
     pa_rtsp_add_header(c->rtsp, "Client-Instance", sci);
     pa_xfree(sci);
     pa_rtsp_set_callback(c->rtsp, rtsp_cb, c);
+
     return pa_rtsp_connect(c->rtsp);
 }
 
-int pa_raop_flush(pa_raop_client* c) {
+int pa_raop_flush(pa_raop_client *c) {
     pa_assert(c);
 
     pa_rtsp_flush(c->rtsp, c->seq, c->rtptime);
     return 0;
 }
 
-int pa_raop_client_set_volume(pa_raop_client* c, pa_volume_t volume) {
+int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume) {
     int rv;
     double db;
     char *param;
@@ -479,13 +481,14 @@ int pa_raop_client_set_volume(pa_raop_client* c, pa_volume_t volume) {
 
     param = pa_sprintf_malloc("volume: %0.6f\r\n",  db);
 
-    /* We just hit and hope, cannot wait for the callback */
+    /* We just hit and hope, cannot wait for the callback. */
     rv = pa_rtsp_setparameter(c->rtsp, param);
     pa_xfree(param);
+
     return rv;
 }
 
-int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchunk* encoded) {
+int pa_raop_client_encode_sample(pa_raop_client *c, pa_memchunk *raw, pa_memchunk *encoded) {
     uint16_t len;
     size_t bufmax;
     uint8_t *bp, bpos;
@@ -513,25 +516,25 @@ int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchun
     bsize = (int)(raw->length / 4);
     length = bsize * 4;
 
-    /* Leave 16 bytes extra to allow for the ALAC header which is about 55 bits */
+    /* Leave 16 bytes extra to allow for the ALAC header which is about 55 bits. */
     bufmax = length + header_size + 16;
     pa_memchunk_reset(encoded);
     encoded->memblock = pa_memblock_new(c->core->mempool, bufmax);
     b = pa_memblock_acquire(encoded->memblock);
     memcpy(b, header, header_size);
 
-    /* Now write the actual samples */
+    /* Now write the actual samples. */
     bp = b + header_size;
     size = bpos = 0;
     bit_writer(&bp,&bpos,&size,1,3); /* channel=1, stereo */
-    bit_writer(&bp,&bpos,&size,0,4); /* unknown */
-    bit_writer(&bp,&bpos,&size,0,8); /* unknown */
-    bit_writer(&bp,&bpos,&size,0,4); /* unknown */
-    bit_writer(&bp,&bpos,&size,1,1); /* hassize */
-    bit_writer(&bp,&bpos,&size,0,2); /* unused */
-    bit_writer(&bp,&bpos,&size,1,1); /* is-not-compressed */
-
-    /* size of data, integer, big endian */
+    bit_writer(&bp,&bpos,&size,0,4); /* Unknown */
+    bit_writer(&bp,&bpos,&size,0,8); /* Unknown */
+    bit_writer(&bp,&bpos,&size,0,4); /* Unknown */
+    bit_writer(&bp,&bpos,&size,1,1); /* Hassize */
+    bit_writer(&bp,&bpos,&size,0,2); /* Unused */
+    bit_writer(&bp,&bpos,&size,1,1); /* Is-not-compressed */
+
+    /* Size of data, integer, big endian. */
     bit_writer(&bp,&bpos,&size,(bsize>>24)&0xff,8);
     bit_writer(&bp,&bpos,&size,(bsize>>16)&0xff,8);
     bit_writer(&bp,&bpos,&size,(bsize>>8)&0xff,8);
@@ -540,7 +543,7 @@ int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchun
     ibp = p = pa_memblock_acquire(raw->memblock);
     maxibp = p + raw->length - 4;
     while (ibp <= maxibp) {
-        /* Byte swap stereo data */
+        /* Byte swap stereo data. */
         bit_writer(&bp,&bpos,&size,*(ibp+1),8);
         bit_writer(&bp,&bpos,&size,*(ibp+0),8);
         bit_writer(&bp,&bpos,&size,*(ibp+3),8);
@@ -552,28 +555,28 @@ int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchun
     pa_memblock_release(raw->memblock);
     encoded->length = header_size + size;
 
-    /* store the length (endian swapped: make this better) */
+    /* Store the length (endian swapped: make this better). */
     len = size + header_size - 4;
     *(b + 2) = len >> 8;
     *(b + 3) = len & 0xff;
 
-    /* encrypt our data */
+    /* Encrypt our data. */
     aes_encrypt(c, (b + header_size), size);
 
-    /* We're done with the chunk */
+    /* We're done with the chunk. */
     pa_memblock_release(encoded->memblock);
 
     return 0;
 }
 
-void pa_raop_client_set_callback(pa_raop_client* c, pa_raop_client_cb_t callback, void *userdata) {
+void pa_raop_client_set_callback(pa_raop_client *c, pa_raop_client_cb_t callback, void *userdata) {
     pa_assert(c);
 
     c->callback = callback;
     c->userdata = userdata;
 }
 
-void pa_raop_client_set_closed_callback(pa_raop_client* c, pa_raop_client_closed_cb_t callback, void *userdata) {
+void pa_raop_client_set_closed_callback(pa_raop_client *c, pa_raop_client_closed_cb_t callback, void *userdata) {
     pa_assert(c);
 
     c->closed_callback = callback;
diff --git a/src/modules/raop/raop_client.h b/src/modules/raop/raop_client.h
index b349a54..6ba32e9 100644
--- a/src/modules/raop/raop_client.h
+++ b/src/modules/raop/raop_client.h
@@ -24,19 +24,19 @@
 
 typedef struct pa_raop_client pa_raop_client;
 
-pa_raop_client* pa_raop_client_new(pa_core *core, const char* host);
-void pa_raop_client_free(pa_raop_client* c);
+pa_raop_client* pa_raop_client_new(pa_core *core, const char *host);
+void pa_raop_client_free(pa_raop_client *c);
 
-int pa_raop_connect(pa_raop_client* c);
-int pa_raop_flush(pa_raop_client* c);
+int pa_raop_connect(pa_raop_client *c);
+int pa_raop_flush(pa_raop_client *c);
 
-int pa_raop_client_set_volume(pa_raop_client* c, pa_volume_t volume);
-int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchunk* encoded);
+int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume);
+int pa_raop_client_encode_sample(pa_raop_client *c, pa_memchunk *raw, pa_memchunk *encoded);
 
 typedef void (*pa_raop_client_cb_t)(int fd, void *userdata);
-void pa_raop_client_set_callback(pa_raop_client* c, pa_raop_client_cb_t callback, void *userdata);
+void pa_raop_client_set_callback(pa_raop_client *c, pa_raop_client_cb_t callback, void *userdata);
 
 typedef void (*pa_raop_client_closed_cb_t)(void *userdata);
-void pa_raop_client_set_closed_callback(pa_raop_client* c, pa_raop_client_closed_cb_t callback, void *userdata);
+void pa_raop_client_set_closed_callback(pa_raop_client *c, pa_raop_client_closed_cb_t callback, void *userdata);
 
 #endif
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index 6f828ba..56f6e15 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -73,7 +73,7 @@ struct pa_rtsp_client {
     char *transport;
 };
 
-pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char* hostname, uint16_t port, const char* useragent) {
+pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char *hostname, uint16_t port, const char *useragent) {
     pa_rtsp_client *c;
 
     pa_assert(mainloop);
@@ -94,7 +94,7 @@ pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char* hostna
     return c;
 }
 
-void pa_rtsp_client_free(pa_rtsp_client* c) {
+void pa_rtsp_client_free(pa_rtsp_client *c) {
     pa_assert(c);
 
     if (c->sc)
@@ -355,25 +355,25 @@ void pa_rtsp_disconnect(pa_rtsp_client *c) {
     c->ioline = NULL;
 }
 
-const char* pa_rtsp_localip(pa_rtsp_client* c) {
+const char* pa_rtsp_localip(pa_rtsp_client *c) {
     pa_assert(c);
 
     return c->localip;
 }
 
-uint32_t pa_rtsp_serverport(pa_rtsp_client* c) {
+uint32_t pa_rtsp_serverport(pa_rtsp_client *c) {
     pa_assert(c);
 
     return c->rtp_port;
 }
 
-void pa_rtsp_set_url(pa_rtsp_client* c, const char* url) {
+void pa_rtsp_set_url(pa_rtsp_client *c, const char *url) {
     pa_assert(c);
 
     c->url = pa_xstrdup(url);
 }
 
-void pa_rtsp_add_header(pa_rtsp_client *c, const char* key, const char* value) {
+void pa_rtsp_add_header(pa_rtsp_client *c, const char *key, const char *value) {
     pa_assert(c);
     pa_assert(key);
     pa_assert(value);
@@ -381,19 +381,19 @@ void pa_rtsp_add_header(pa_rtsp_client *c, const char* key, const char* value) {
     pa_headerlist_puts(c->headers, key, value);
 }
 
-void pa_rtsp_remove_header(pa_rtsp_client *c, const char* key) {
+void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key) {
     pa_assert(c);
     pa_assert(key);
 
     pa_headerlist_remove(c->headers, key);
 }
 
-static int rtsp_exec(pa_rtsp_client* c, const char* cmd,
-                        const char* content_type, const char* content,
+static int rtsp_exec(pa_rtsp_client *c, const char *cmd,
+                        const char *content_type, const char *content,
                         int expect_response,
-                        pa_headerlist* headers) {
-    pa_strbuf* buf;
-    char* hdrs;
+                        pa_headerlist *headers) {
+    pa_strbuf *buf;
+    char *hdrs;
 
     pa_assert(c);
     pa_assert(c->url);
@@ -443,17 +443,22 @@ static int rtsp_exec(pa_rtsp_client* c, const char* cmd,
     return 0;
 }
 
-int pa_rtsp_announce(pa_rtsp_client *c, const char* sdp) {
+int pa_rtsp_announce(pa_rtsp_client *c, const char *sdp) {
+    int rv;
+
     pa_assert(c);
+
     if (!sdp)
         return -1;
 
     c->state = STATE_ANNOUNCE;
-    return rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL);
+    rv = rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL);
+
+    return rv;
 }
 
-int pa_rtsp_setup(pa_rtsp_client* c) {
-    pa_headerlist* headers;
+int pa_rtsp_setup(pa_rtsp_client *c) {
+    pa_headerlist *headers;
     int rv;
 
     pa_assert(c);
@@ -463,16 +468,18 @@ int pa_rtsp_setup(pa_rtsp_client* c) {
 
     c->state = STATE_SETUP;
     rv = rtsp_exec(c, "SETUP", NULL, NULL, 1, headers);
+
     pa_headerlist_free(headers);
     return rv;
 }
 
-int pa_rtsp_record(pa_rtsp_client* c, uint16_t* seq, uint32_t* rtptime) {
-    pa_headerlist* headers;
-    int rv;
+int pa_rtsp_record(pa_rtsp_client *c, uint16_t *seq, uint32_t *rtptime) {
+    pa_headerlist *headers;
     char *info;
+    int rv;
 
     pa_assert(c);
+
     if (!c->session) {
         /* No session in progress */
         return -1;
@@ -489,30 +496,40 @@ int pa_rtsp_record(pa_rtsp_client* c, uint16_t* seq, uint32_t* rtptime) {
 
     c->state = STATE_RECORD;
     rv = rtsp_exec(c, "RECORD", NULL, NULL, 1, headers);
+
     pa_headerlist_free(headers);
     return rv;
 }
 
 int pa_rtsp_teardown(pa_rtsp_client *c) {
+    int rv;
+
     pa_assert(c);
 
     c->state = STATE_TEARDOWN;
-    return rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
+    rv = rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
+
+    return rv;
 }
 
-int pa_rtsp_setparameter(pa_rtsp_client *c, const char* param) {
+int pa_rtsp_setparameter(pa_rtsp_client *c, const char *param) {
+    int rv;
+
     pa_assert(c);
+
     if (!param)
         return -1;
 
     c->state = STATE_SET_PARAMETER;
-    return rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL);
+    rv = rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL);
+
+    return rv;
 }
 
 int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime) {
     pa_headerlist* headers;
-    int rv;
     char *info;
+    int rv;
 
     pa_assert(c);
 
@@ -523,6 +540,7 @@ int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime) {
 
     c->state = STATE_FLUSH;
     rv = rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers);
+
     pa_headerlist_free(headers);
     return rv;
 }
diff --git a/src/modules/rtp/rtsp_client.h b/src/modules/rtp/rtsp_client.h
index f0ff95a..d51acda 100644
--- a/src/modules/rtp/rtsp_client.h
+++ b/src/modules/rtp/rtsp_client.h
@@ -41,29 +41,29 @@ typedef enum {
   STATE_SET_PARAMETER,
   STATE_DISCONNECTED
 } pa_rtsp_state;
-typedef void (*pa_rtsp_cb_t)(pa_rtsp_client *c, pa_rtsp_state state, pa_headerlist* hl, void *userdata);
+typedef void (*pa_rtsp_cb_t)(pa_rtsp_client *c, pa_rtsp_state state, pa_headerlist *hl, void *userdata);
 
-pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char* hostname, uint16_t port, const char* useragent);
-void pa_rtsp_client_free(pa_rtsp_client* c);
+pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char *hostname, uint16_t port, const char *useragent);
+void pa_rtsp_client_free(pa_rtsp_client *c);
 
-int pa_rtsp_connect(pa_rtsp_client* c);
+int pa_rtsp_connect(pa_rtsp_client *c);
 void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userdata);
 
-void pa_rtsp_disconnect(pa_rtsp_client* c);
+void pa_rtsp_disconnect(pa_rtsp_client *c);
 
-const char* pa_rtsp_localip(pa_rtsp_client* c);
-uint32_t pa_rtsp_serverport(pa_rtsp_client* c);
-void pa_rtsp_set_url(pa_rtsp_client* c, const char* url);
-void pa_rtsp_add_header(pa_rtsp_client *c, const char* key, const char* value);
-void pa_rtsp_remove_header(pa_rtsp_client *c, const char* key);
+const char* pa_rtsp_localip(pa_rtsp_client *c);
+uint32_t pa_rtsp_serverport(pa_rtsp_client *c);
+void pa_rtsp_set_url(pa_rtsp_client *c, const char *url);
+void pa_rtsp_add_header(pa_rtsp_client *c, const char *key, const char *value);
+void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key);
 
-int pa_rtsp_announce(pa_rtsp_client* c, const char* sdp);
+int pa_rtsp_announce(pa_rtsp_client *c, const char *sdp);
 
-int pa_rtsp_setup(pa_rtsp_client* c);
-int pa_rtsp_record(pa_rtsp_client* c, uint16_t* seq, uint32_t* rtptime);
-int pa_rtsp_teardown(pa_rtsp_client* c);
+int pa_rtsp_setup(pa_rtsp_client *c);
+int pa_rtsp_record(pa_rtsp_client *c, uint16_t *seq, uint32_t *rtptime);
+int pa_rtsp_teardown(pa_rtsp_client *c);
 
-int pa_rtsp_setparameter(pa_rtsp_client* c, const char* param);
-int pa_rtsp_flush(pa_rtsp_client* c, uint16_t seq, uint32_t rtptime);
+int pa_rtsp_setparameter(pa_rtsp_client *c, const char *param);
+int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime);
 
 #endif

commit d623c689e6b848fff108bc545ba7d7c34b6db699
Author: Hajime Fujita <crisp.fujita at nifty.com>
Date:   Sun Jan 31 22:15:59 2016 -0600

    rtp: Freeing ioline when disconnecting
    
    pa_ioline_close does not free the ioline structure itself, so we
    have to unref the structure if we want to free it.
    
    Reviewed-by: Anton Lundin <glance at acc.umu.se>

diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index bdfd24f..6f828ba 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -348,8 +348,10 @@ void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userda
 void pa_rtsp_disconnect(pa_rtsp_client *c) {
     pa_assert(c);
 
-    if (c->ioline)
+    if (c->ioline) {
         pa_ioline_close(c->ioline);
+        pa_ioline_unref(c->ioline);
+    }
     c->ioline = NULL;
 }
 

commit 017ed3e7fa50bbb6e1e2b6731e5cfa79d5e3f459
Author: Hajime Fujita <crisp.fujita at nifty.com>
Date:   Sun Jan 31 22:15:58 2016 -0600

    Support IPv6 address in pa_socket_client_new_string()
    
    pa_socket_client_new_string() did not work as expected when an IPv6
    address string like "2001:db8::1" is passed as the "name" parameter.
    This is because the name parameter is then passed to pa_parse_address(),
    which thinks the last colon as a separator between hostname (or address)
    and a port number. To prevent pa_parse_address() from doing this, an IPv6
    address must be bracketed with "[]" (e.g. "[2001:db8::1]"). [1]
    
    This patch fixes pa_socket_client_new_string() so that it internally
    adds brackets to an IPv6 address. This decision is based on a
    discussion at [2].
    
    [1]: http://lists.freedesktop.org/archives/pulseaudio-discuss/2014-October/022010.html
    [2]: http://lists.freedesktop.org/archives/pulseaudio-discuss/2014-November/022401.html
    
    Reviewed-by: Anton Lundin <glance at acc.umu.se>

diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c
index 7f72e79..b909f52 100644
--- a/src/pulsecore/parseaddr.c
+++ b/src/pulsecore/parseaddr.c
@@ -143,3 +143,14 @@ bool pa_is_ip_address(const char *a) {
 
     return false;
 }
+
+bool pa_is_ip6_address(const char *a) {
+    char buf[INET6_ADDRSTRLEN];
+
+    pa_assert(a);
+
+    if (inet_pton(AF_INET6, a, buf) >= 1)
+        return true;
+
+    return false;
+}
diff --git a/src/pulsecore/parseaddr.h b/src/pulsecore/parseaddr.h
index 5d2c358..6bb4d85 100644
--- a/src/pulsecore/parseaddr.h
+++ b/src/pulsecore/parseaddr.h
@@ -41,4 +41,6 @@ int pa_parse_address(const char *a, pa_parsed_address *ret_p);
 
 bool pa_is_ip_address(const char *a);
 
+bool pa_is_ip6_address(const char *a);
+
 #endif
diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c
index b4067f0..c87406d 100644
--- a/src/pulsecore/socket-client.c
+++ b/src/pulsecore/socket-client.c
@@ -428,12 +428,28 @@ static void start_timeout(pa_socket_client *c, bool use_rtclock) {
 pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, bool use_rtclock, const char*name, uint16_t default_port) {
     pa_socket_client *c = NULL;
     pa_parsed_address a;
+    char *name_buf;
 
     pa_assert(m);
     pa_assert(name);
 
-    if (pa_parse_address(name, &a) < 0)
-        return NULL;
+    a.path_or_host = NULL;
+
+    if (pa_is_ip6_address(name)) {
+        size_t len = strlen(name);
+        name_buf = pa_xmalloc(len + 3);
+        memcpy(name_buf + 1, name, len);
+        name_buf[0] = '[';
+        name_buf[len + 1] = ']';
+        name_buf[len + 2] = '\0';
+    } else {
+        name_buf = pa_xstrdup(name);
+    }
+
+    if (pa_parse_address(name_buf, &a) < 0) {
+        pa_log_warn("parsing address failed: %s", name_buf);
+        goto finish;
+    }
 
     if (!a.port)
         a.port = default_port;
@@ -530,6 +546,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, bool use_rtclo
     }
 
 finish:
+    pa_xfree(name_buf);
     pa_xfree(a.path_or_host);
     return c;
 



More information about the pulseaudio-commits mailing list