[pulseaudio-commits] [SCM] PulseAudio Sound Server branch, master, updated. v0.9.13-90-g8e3e88d

Lennart Poettering gitmailer-noreply at 0pointer.de
Wed Oct 8 14:03:50 PDT 2008


This is an automated email from the git hooks/post-receive script. It was
generated because of a push to the "PulseAudio Sound Server" repository.

The master branch has been updated
      from  91b64bc96b941692f7fd507e4afbe3bd1b9f9001 (commit)

- Log -----------------------------------------------------------------
8e3e88d... Merge commit 'coling/airtunes-0.9.13'
59eb649... Follow master change r34dd4a and fix shutdown when --disallow-module-loading=1 is passed
8715121... Modularise the RAOP stuff that requires OpenSSL and make it optional at compile time
c3d8bb5... Remove $Id$ lines left over from SVN
19d2831... Make module-raop-sink/discover work with 0.9.11 API
ded09d1... Implement hardware volume control. This allows near instant change of volume when controlling the hardware but the stream volume still suffers from a sizable delay.
e543e04... Implement a set volume function to expose this capability to higher layers
36f2aad... Use the new pa_namereg_make_valid_name() function.
0ff75ae... Add Lennart back in to Copyright as I copied these files from his originals and was a bit overzealous in changing things ;)
729bbaf... Automatic discovery of airtunes devices via Bonjour/Avahi.
d997420... Minor correction of help text
15e8420... Still send silence when we are not doing anything else, but also flush the buffers correctly upon recovery from suspension.
c49be78... Add some new public API functions to connect and flush.
d86fc75... Change the API of the RTSP client a bit.
19dcb52... Remove unneeded headers accidentially added in r2500.
5f527dc... Add seq and rtptime params to record/flush with a view to using these for timing and device suspension
651da7d... Minor update to copywrite (I still plan to replace this completely but in the mean time....)
7f0cf0c... Fix up a couple of values related to encoding overhead.
13bc075... A few related changes:
b93e9e8... Keep track of the memblock pointer internally and do not rely on subsequent calls to pass it back in for unref'ing
8108121... Set forgotten keyword property
6dc5e07... Set the send buffer size to prevent rendering silence in amongst our good data (this should be more sophisticated but that can wait for a glitch-free port)
6c1dd6e... Move the encoding loop around a bit such that it does not grab the data and keep it for the next loop iteration.
3767cdb... Do tidy up on disconnection.
9216684... Do not prefix internal function rtsp_exec.
eca94fe... Don't try to free stack variables.
be73d37... unref the raw data memblock before requesting more data.
cb8c5a9... Some misc fixes. consts, base64 optimisation (not that it will be with us long anyway), and c comments
4b7b7b1... Fix up IPv6 address format to enclose it in []
d195d06... Change suggested by Lennart. Do not return a memchunk, instead pass in the pointer.
e00127f... Various changes suggested by Lennart.
ec9a618... Listen to the on_close callback. This still causes asserts in the mainloop, so this is not a complete solution
899492c... Add a new callback structure to propigate when the RTSP connection dies
5eecfa2... Move the ownership of the encoded data memchunk into the raop_client.
4dd3185... Do not assert on NULL values of s. This means the connection was closed. This change somehow kills the mainloop with an assert, so I need to sort that out.
d51f594... A very rough first version of the sink.
264a1c2... Add more libraries to librtp now that it's doing a lot more.
f97c5de... Properly duplicate the hostname passed in on connect.
1fb0465... Combine pa_raop_client_new and pa_raop_client_connect (no point in having them separate)
41e31ab... Rename rtsp.{c,h} to rtsp_client.{c,h}.
e596f42... Wrap the io_callback to ensure that all data is written before asking for more.
6510d97... Use a more stateful response parser.
22e299a... Add a pa_iochannel callback for when the RAOP connection connects.
8fb58e3... Add a function for packing bits into a byte buffer. This will be needed when encoding the audio data in ALAC format.
66cf1d1... Some minor tidyup to remove code now in raop client. Still nowhere near functional.
20478a4... Add a skeleton raop client which builds on the rtsp client.
d423605... Move closer to an asynchronous structure (still some parsing code to be converted).
a0d3582... Trivial change to allocate memory using pulse methods.
a08d733... Fix svn properties and some minor indentation
27ed970... Convert the return values to fit with the rest of pulse 0 == success, < 0 == failure
405cf72... Convert to using pa_socket_client rather than using blocking IO.
ce9a41e... Use _free rather than _destroy so as not to mix naming conventions.
91edf9e... Use pa_sprintf_malloc to do simple concatenation rather than using the higher overhead of pa_strbuf
6570620... Start the raop sink. It's based on pipe sink and isn't anywhere near finished. It does however compile.
fef102e... Add a simple base64 library that will be used by the sink
4847706... Add a RTSP client impelmentation.
8c1c565... Add a small lib to interpret and produce headers as used in http style requests.
-----------------------------------------------------------------------

Summary of changes:
 configure.ac                                       |   41 ++
 src/Makefile.am                                    |   45 ++-
 ...-zeroconf-discover.c => module-raop-discover.c} |  110 +---
 .../{module-esound-sink.c => module-raop-sink.c}   |  520 +++++++++---------
 src/modules/raop/base64.c                          |  126 +++++
 src/{daemon/caps.h => modules/raop/base64.h}       |   18 +-
 src/modules/raop/raop_client.c                     |  561 ++++++++++++++++++++
 src/modules/raop/raop_client.h                     |   46 ++
 src/modules/rtp/headerlist.c                       |  186 +++++++
 .../rtclock.h => modules/rtp/headerlist.h}         |   28 +-
 src/modules/rtp/rtsp_client.c                      |  542 +++++++++++++++++++
 src/modules/rtp/rtsp_client.h                      |   73 +++
 12 files changed, 1932 insertions(+), 364 deletions(-)
 copy src/modules/{module-zeroconf-discover.c => module-raop-discover.c} (71%)
 copy src/modules/{module-esound-sink.c => module-raop-sink.c} (51%)
 create mode 100644 src/modules/raop/base64.c
 copy src/{daemon/caps.h => modules/raop/base64.h} (70%)
 create mode 100644 src/modules/raop/raop_client.c
 create mode 100644 src/modules/raop/raop_client.h
 create mode 100644 src/modules/rtp/headerlist.c
 copy src/{pulsecore/rtclock.h => modules/rtp/headerlist.h} (52%)
 create mode 100644 src/modules/rtp/rtsp_client.c
 create mode 100644 src/modules/rtp/rtsp_client.h

-----------------------------------------------------------------------

commit 8c1c565c86ddeb0c2b3b89d53aed0052eca1ebbf
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Thu May 1 23:35:24 2008 +0000

    Add a small lib to interpret and produce headers as used in http style requests.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2332 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/headerlist.c b/src/modules/rtp/headerlist.c
new file mode 100644
index 0000000..9ea17ae
--- /dev/null
+++ b/src/modules/rtp/headerlist.c
@@ -0,0 +1,190 @@
+/* $Id$ */
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Colin Guthrie
+  Copyright 2007 Lennart Poettering
+
+  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
+  Lesser 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 <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/hashmap.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/core-util.h>
+
+#include "headerlist.h"
+
+struct header {
+    char *key;
+    void *value;
+    size_t nbytes;
+};
+
+#define MAKE_HASHMAP(p) ((pa_hashmap*) (p))
+#define MAKE_HEADERLIST(p) ((pa_headerlist*) (p))
+
+static void header_free(struct header *hdr) {
+    pa_assert(hdr);
+
+    pa_xfree(hdr->key);
+    pa_xfree(hdr->value);
+    pa_xfree(hdr);
+}
+
+pa_headerlist* pa_headerlist_new(void) {
+    return MAKE_HEADERLIST(pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func));
+}
+
+void pa_headerlist_free(pa_headerlist* p) {
+    struct header *hdr;
+
+    while ((hdr = pa_hashmap_steal_first(MAKE_HASHMAP(p))))
+        header_free(hdr);
+
+    pa_hashmap_free(MAKE_HASHMAP(p), NULL, NULL);
+}
+
+int pa_headerlist_puts(pa_headerlist *p, const char *key, const char *value) {
+    struct header *hdr;
+    pa_bool_t add = FALSE;
+
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!(hdr = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+        hdr = pa_xnew(struct header, 1);
+        hdr->key = pa_xstrdup(key);
+        add = TRUE;
+    } else
+        pa_xfree(hdr->value);
+
+    hdr->value = pa_xstrdup(value);
+    hdr->nbytes = strlen(value)+1;
+
+    if (add)
+        pa_hashmap_put(MAKE_HASHMAP(p), hdr->key, hdr);
+
+    return 0;
+}
+
+int pa_headerlist_putsappend(pa_headerlist *p, const char *key, const char *value) {
+    struct header *hdr;
+    pa_bool_t add = FALSE;
+    pa_strbuf *buf;
+
+    pa_assert(p);
+    pa_assert(key);
+
+    buf = pa_strbuf_new();
+    if (!(hdr = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+        hdr = pa_xnew(struct header, 1);
+        hdr->key = pa_xstrdup(key);
+        add = TRUE;
+    } else {
+        pa_strbuf_puts(buf, hdr->value);
+        pa_xfree(hdr->value);
+    }
+    pa_strbuf_puts(buf, value);
+    hdr->value = pa_strbuf_tostring_free(buf);
+    hdr->nbytes = strlen(hdr->value)+1;
+
+    if (add)
+        pa_hashmap_put(MAKE_HASHMAP(p), hdr->key, hdr);
+
+    return 0;
+}
+
+const char *pa_headerlist_gets(pa_headerlist *p, const char *key) {
+    struct header *hdr;
+
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!(hdr = pa_hashmap_get(MAKE_HASHMAP(p), key)))
+        return NULL;
+
+    if (hdr->nbytes <= 0)
+        return NULL;
+
+    if (((char*) hdr->value)[hdr->nbytes-1] != 0)
+        return NULL;
+
+    if (strlen((char*) hdr->value) != hdr->nbytes-1)
+        return NULL;
+
+    return (char*) hdr->value;
+}
+
+int pa_headerlist_remove(pa_headerlist *p, const char *key) {
+    struct header *hdr;
+
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!(hdr = pa_hashmap_remove(MAKE_HASHMAP(p), key)))
+        return -1;
+
+    header_free(hdr);
+    return 0;
+}
+
+const char *pa_headerlist_iterate(pa_headerlist *p, void **state) {
+    struct header *hdr;
+
+    if (!(hdr = pa_hashmap_iterate(MAKE_HASHMAP(p), state, NULL)))
+        return NULL;
+
+    return hdr->key;
+}
+
+char *pa_headerlist_to_string(pa_headerlist *p) {
+    const char *key;
+    void *state = NULL;
+    pa_strbuf *buf;
+
+    pa_assert(p);
+
+    buf = pa_strbuf_new();
+
+    while ((key = pa_headerlist_iterate(p, &state))) {
+
+        const char *v;
+
+        if ((v = pa_headerlist_gets(p, key)))
+            pa_strbuf_printf(buf, "%s: %s\r\n", key, v);
+    }
+
+    return pa_strbuf_tostring_free(buf);
+}
+
+int pa_headerlist_contains(pa_headerlist *p, const char *key) {
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!(pa_hashmap_get(MAKE_HASHMAP(p), key)))
+        return 0;
+
+    return 1;
+}
diff --git a/src/modules/rtp/headerlist.h b/src/modules/rtp/headerlist.h
new file mode 100644
index 0000000..276d0e3
--- /dev/null
+++ b/src/modules/rtp/headerlist.h
@@ -0,0 +1,48 @@
+#ifndef foopulseheaderlisthfoo
+#define foopulseheaderlisthfoo
+
+/* $Id$ */
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Colin Guthrie
+  Copyright 2007 Lennart Poettering
+
+  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
+  Lesser 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/macro.h>
+
+typedef struct pa_headerlist pa_headerlist;
+
+pa_headerlist* pa_headerlist_new(void);
+void pa_headerlist_free(pa_headerlist* p);
+
+int pa_headerlist_puts(pa_headerlist *p, const char *key, const char *value);
+int pa_headerlist_putsappend(pa_headerlist *p, const char *key, const char *value);
+
+const char *pa_headerlist_gets(pa_headerlist *p, const char *key);
+
+int pa_headerlist_remove(pa_headerlist *p, const char *key);
+
+const char *pa_headerlist_iterate(pa_headerlist *p, void **state);
+
+char *pa_headerlist_to_string(pa_headerlist *p);
+
+int pa_headerlist_contains(pa_headerlist *p, const char *key);
+
+#endif

commit 48477067ee20678a9c741da4e75dbbcdd6b01efe
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Thu May 1 23:40:19 2008 +0000

    Add a RTSP client impelmentation.
    
    I still need to adapt the header reading to move the concatenation code to the headerlist lib
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2333 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/rtsp.c b/src/modules/rtp/rtsp.c
new file mode 100644
index 0000000..3fd1ba0
--- /dev/null
+++ b/src/modules/rtp/rtsp.c
@@ -0,0 +1,472 @@
+/* $Id$ */
+
+/***
+  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 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 <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/poll.h>
+
+#include "rtsp.h"
+
+/*
+ * read one line from the file descriptor
+ * timeout: msec unit, -1 for infinite
+ * if CR comes then following LF is expected
+ * returned string in line is always null terminated, maxlen-1 is maximum string length
+ */
+static int pa_read_line(int fd, char *line, int maxlen, int timeout)
+{
+    int i, rval;
+    int count;
+    char ch;
+    struct pollfd pfds;
+    count = 0;
+    *line = 0;
+    pfds.events = POLLIN;
+    pfds.fd = fd;
+
+    for (i=0; i<maxlen; ++i) {
+        if (!poll(&pfds, 1, timeout))
+            return 0;
+
+        rval = read(fd, &ch, 1);
+
+        if (-1 == rval) {
+            if (EAGAIN == errno)
+                return 0;
+            //ERRMSG("%s:read error: %s\n", __func__, strerror(errno));
+            return -1;
+        }
+
+        if (0 == rval) {
+            //INFMSG("%s:disconnected on the other end\n", __func__);
+            return -1;
+        }
+
+        if ('\n' == ch) {
+            *line = 0;
+            return count;
+        }
+
+        if ('\r' == ch)
+            continue;
+
+        *line++ = ch;
+        count++;
+
+        if (count >= maxlen-1)
+            break;
+    }
+
+    *line = 0;
+    return count;
+}
+
+
+static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
+                        const char* content_type, const char* content,
+                        int expect_response,
+                        pa_headerlist* headers, pa_headerlist** response_headers) {
+    pa_strbuf* buf;
+    char* hdrs;
+    ssize_t l;
+    char response[1024];
+    int timeout;
+    char* token;
+    const char* token_state;
+    char delimiters[2];
+    char* header;
+    char* delimpos;
+
+
+    pa_assert(c);
+    pa_assert(c->url);
+
+    if (!cmd)
+        return 0;
+
+    buf = pa_strbuf_new();
+    pa_strbuf_printf(buf, "%s %s RTSP/1.0\r\nCSeq: %d\r\n", cmd, c->url, ++c->cseq);
+    if (c->session)
+        pa_strbuf_printf(buf, "Session: %s\r\n", c->session);
+
+    // Add the headers
+    if (headers) {
+        hdrs = pa_headerlist_to_string(headers);
+        pa_strbuf_puts(buf, hdrs);
+        pa_xfree(hdrs);
+    }
+
+    if (content_type && content) {
+        pa_strbuf_printf(buf, "Content-Type: %s\r\nContent-Length: %d\r\n",
+          content_type, (int)strlen(content));
+    }
+
+    pa_strbuf_printf(buf, "User-Agent: %s\r\n", c->useragent);
+
+    if (c->headers) {
+        hdrs = pa_headerlist_to_string(c->headers);
+        pa_strbuf_puts(buf, hdrs);
+        pa_xfree(hdrs);
+    }
+
+    pa_strbuf_puts(buf, "\r\n");
+
+    if (content_type && content) {
+        pa_strbuf_puts(buf, content);
+    }
+
+    // Our packet is created... now we can send it :)
+    hdrs = pa_strbuf_tostring_free(buf);
+    l = pa_write(c->fd, hdrs, strlen(hdrs), NULL);
+    pa_xfree(hdrs);
+
+    // Do we expect a response?
+    if (!expect_response)
+        return 1;
+
+    timeout = 5000;
+    if (pa_read_line(c->fd, response, sizeof(response), timeout) <= 0) {
+        //ERRMSG("%s: request failed\n",__func__);
+        return 0;
+    }
+
+    delimiters[0] = ' ';
+    delimiters[1] = '\0';
+    token_state = NULL;
+    pa_xfree(pa_split(response, delimiters, &token_state));
+    token = pa_split(response, delimiters, &token_state);
+    if (!token || strcmp(token, "200")) {
+        pa_xfree(token);
+        //ERRMSG("%s: request failed, error %s\n",__func__,token);
+        return 0;
+    }
+    pa_xfree(token);
+
+    // We want to return the headers?
+    if (!response_headers)
+    {
+        // We have no storage, so just clear out the response.
+        while (pa_read_line(c->fd, response, sizeof(response), timeout) > 0) {
+            // Reduce timeout for future requests
+            timeout = 1000;
+        }
+        return 1;
+    }
+
+    header = NULL;
+    buf = pa_strbuf_new();
+    while (pa_read_line(c->fd, response, sizeof(response), timeout) > 0) {
+        // Reduce timeout for future requests
+        timeout = 1000;
+
+        // If the first character is a space, it's a continuation header
+        if (header && ' ' == response[0]) {
+            // Add this line to the buffer (sans the space.
+            pa_strbuf_puts(buf, &(response[1]));
+            continue;
+        }
+
+        if (header) {
+            // This is not a continuation header so let's dump the full header/value into our proplist
+            pa_headerlist_puts(*response_headers, header, pa_strbuf_tostring_free(buf));
+            pa_xfree(header);
+            //header = NULL;
+            buf = pa_strbuf_new();
+        }
+
+        delimpos = strstr(response, ":");
+        if (!delimpos) {
+            //ERRMSG("%s: Request failed, bad header\n",__func__);
+            return 0;
+        }
+
+        if (strlen(delimpos) > 1) {
+            // Cut our line off so we can copy the header name out
+            *delimpos++ = '\0';
+
+            // Trim the front of any spaces
+            while (' ' == *delimpos)
+                ++delimpos;
+
+            pa_strbuf_puts(buf, delimpos);
+        } else {
+            // Cut our line off so we can copy the header name out
+            *delimpos = '\0';
+        }
+
+        // Save the header name
+        header = pa_xstrdup(response);
+    }
+    // We will have a header left from our looping itteration, so add it in :)
+    if (header) {
+        // This is not a continuation header so let's dump it into our proplist
+        pa_headerlist_puts(*response_headers, header, pa_strbuf_tostring(buf));
+    }
+    pa_strbuf_free(buf);
+
+    return 1;
+}
+
+
+pa_rtsp_context* pa_rtsp_context_new(const char* useragent) {
+    pa_rtsp_context *c;
+
+    c = pa_xnew0(pa_rtsp_context, 1);
+    c->fd = -1;
+    c->headers = pa_headerlist_new();
+
+    if (useragent)
+        c->useragent = useragent;
+    else
+        c->useragent = "PulseAudio RTSP Client";
+
+    return c;
+}
+
+
+void pa_rtsp_context_destroy(pa_rtsp_context* c) {
+    if (c) {
+        pa_xfree(c->url);
+        pa_xfree(c->session);
+        pa_xfree(c->transport);
+        pa_headerlist_free(c->headers);
+    }
+    pa_xfree(c);
+}
+
+
+int pa_rtsp_connect(pa_rtsp_context *c, const char* hostname, uint16_t port, const char* sid) {
+    struct sockaddr_in sa;
+    struct sockaddr_in name;
+    socklen_t namelen = sizeof(name);
+    struct hostent *host = NULL;
+    int r;
+
+    pa_assert(c);
+    pa_assert(hostname);
+    pa_assert(port > 0);
+    pa_assert(sid);
+
+    memset(&sa, 0, sizeof(sa));
+    sa.sin_family = AF_INET;
+    sa.sin_port = htons(port);
+
+    host = gethostbyname(hostname);
+    if (!host) {
+        unsigned int addr = inet_addr(hostname);
+        if (addr != INADDR_NONE)
+            host = gethostbyaddr((char*)&addr, 4, AF_INET);
+        if (!host)
+            return 0;
+    }
+    memcpy(&sa.sin_addr, host->h_addr, sizeof(struct in_addr));
+
+    if ((c->fd = socket(sa.sin_family, SOCK_STREAM, 0)) < 0) {
+        pa_log("socket(): %s", pa_cstrerror(errno));
+        return 0;
+    }
+
+    // Q: is FD_CLOEXEC reqd?
+    pa_make_fd_cloexec(c->fd);
+    pa_make_tcp_socket_low_delay(c->fd);
+
+    if ((r = connect(c->fd, &sa, sizeof(struct sockaddr_in))) < 0) {
+#ifdef OS_IS_WIN32
+        if (WSAGetLastError() != EWOULDBLOCK) {
+            pa_log_debug("connect(): %d", WSAGetLastError());
+#else
+        if (errno != EINPROGRESS) {
+            pa_log_debug("connect(): %s (%d)", pa_cstrerror(errno), errno);
+#endif
+            pa_close(c->fd);
+            c->fd = -1;
+            return 0;
+        }
+    }
+
+    if (0 != getsockname(c->fd, (struct sockaddr*)&name, &namelen)) {
+        pa_close(c->fd);
+        c->fd = -1;
+        return 0;
+    }
+    memcpy(&c->local_addr, &name.sin_addr, sizeof(struct in_addr));
+    c->url = pa_sprintf_malloc("rtsp://%s/%s", inet_ntoa(name.sin_addr), sid);
+
+    return 1;
+}
+
+
+void pa_rtsp_disconnect(pa_rtsp_context *c) {
+    pa_assert(c);
+
+    if (c->fd < 0)
+      return;
+    pa_close(c->fd);
+    c->fd = -1;
+}
+
+
+const char* pa_rtsp_localip(pa_rtsp_context* c) {
+    pa_assert(c);
+
+    if (c->fd < 0)
+        return NULL;
+    return inet_ntoa(c->local_addr);
+}
+
+
+int pa_rtsp_announce(pa_rtsp_context *c, const char* sdp) {
+    pa_assert(c);
+    if (!sdp)
+        return 0;
+
+    return pa_rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL, NULL);
+}
+
+
+int pa_rtsp_setup(pa_rtsp_context* c, pa_headerlist** response_headers) {
+    pa_headerlist* headers;
+    pa_headerlist* rheaders;
+    char delimiters[2];
+    char* token;
+    const char* token_state;
+    const char* pc;
+
+    pa_assert(c);
+
+    headers = pa_headerlist_new();
+    rheaders = pa_headerlist_new();
+    pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
+
+    if (!pa_rtsp_exec(c, "SETUP", NULL, NULL, 1, headers, &rheaders)) {
+        pa_headerlist_free(headers);
+        pa_headerlist_free(rheaders);
+        return 0;
+    }
+    pa_headerlist_free(headers);
+
+    c->session = pa_xstrdup(pa_headerlist_gets(rheaders, "Session"));
+    c->transport = pa_xstrdup(pa_headerlist_gets(rheaders, "Transport"));
+
+    if (!c->session || !c->transport) {
+        pa_headerlist_free(rheaders);
+        return 0;
+    }
+
+    // Now parse out the server port component of the response.
+    c->port = 0;
+    delimiters[0] = ';';
+    delimiters[1] = '\0';
+    token_state = NULL;
+    while ((token = pa_split(c->transport, delimiters, &token_state))) {
+        if ((pc = strstr(token, "="))) {
+            if (0 == strncmp(token, "server_port", 11)) {
+                pa_atou(pc+1, &c->port);
+                pa_xfree(token);
+                break;
+            }
+        }
+        pa_xfree(token);
+    }
+    if (0 == c->port) {
+        // Error no server_port in response
+        pa_headerlist_free(rheaders);
+        return 0;
+    }
+
+    *response_headers = rheaders;
+    return 1;
+}
+
+
+int pa_rtsp_record(pa_rtsp_context* c) {
+    pa_headerlist* headers;
+    int rv;
+
+    pa_assert(c);
+    if (!c->session) {
+        // No seesion in progres
+        return 0;
+    }
+
+    headers = pa_headerlist_new();
+    pa_headerlist_puts(headers, "Range", "npt=0-");
+    pa_headerlist_puts(headers, "RTP-Info", "seq=0;rtptime=0");
+
+    rv = pa_rtsp_exec(c, "RECORD", NULL, NULL, 1, headers, NULL);
+    pa_headerlist_free(headers);
+    return rv;
+}
+
+
+int pa_rtsp_teardown(pa_rtsp_context *c) {
+    pa_assert(c);
+
+    return pa_rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL, NULL);
+}
+
+
+int pa_rtsp_setparameter(pa_rtsp_context *c, const char* param) {
+    pa_assert(c);
+    if (!param)
+        return 0;
+
+    return pa_rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL, NULL);
+}
+
+
+int pa_rtsp_flush(pa_rtsp_context *c) {
+    pa_headerlist* headers;
+    int rv;
+
+    pa_assert(c);
+
+    headers = pa_headerlist_new();
+    pa_headerlist_puts(headers, "RTP-Info", "seq=0;rtptime=0");
+
+    rv = pa_rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers, NULL);
+    pa_headerlist_free(headers);
+    return rv;
+}
diff --git a/src/modules/rtp/rtsp.h b/src/modules/rtp/rtsp.h
new file mode 100644
index 0000000..504f144
--- /dev/null
+++ b/src/modules/rtp/rtsp.h
@@ -0,0 +1,66 @@
+#ifndef foortsphfoo
+#define foortsphfoo
+
+/* $Id: rtp.h 1465 2007-05-29 17:24:48Z lennart $ */
+
+/***
+  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 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 <inttypes.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netdb.h>
+
+#include <pulsecore/memblockq.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/socket-client.h>
+
+#include "headerlist.h"
+
+typedef struct pa_rtsp_context {
+    int fd;
+    const char* useragent;
+    pa_headerlist* headers;
+    char* url;
+    uint32_t port;
+    uint32_t cseq;
+    char* session;
+    char* transport;
+    struct in_addr local_addr;
+} pa_rtsp_context;
+
+pa_rtsp_context* pa_rtsp_context_new(const char* useragent);
+void pa_rtsp_context_destroy(pa_rtsp_context* c);
+
+int pa_rtsp_connect(pa_rtsp_context* c, const char* hostname, uint16_t port, const char* sid);
+void pa_rtsp_disconnect(pa_rtsp_context* c);
+
+const char* pa_rtsp_localip(pa_rtsp_context* c);
+int pa_rtsp_announce(pa_rtsp_context* c, const char* sdp);
+
+int pa_rtsp_setup(pa_rtsp_context* c, pa_headerlist** response_headers);
+int pa_rtsp_record(pa_rtsp_context* c);
+int pa_rtsp_teardown(pa_rtsp_context* c);
+
+int pa_rtsp_setparameter(pa_rtsp_context* c, const char* param);
+int pa_rtsp_flush(pa_rtsp_context* c);
+
+#endif

commit fef102e35ae4adff8ab000c628cb659c337af51d
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Thu May 1 23:43:34 2008 +0000

    Add a simple base64 library that will be used by the sink
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2334 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/base64.c b/src/modules/rtp/base64.c
new file mode 100644
index 0000000..ec9f221
--- /dev/null
+++ b/src/modules/rtp/base64.c
@@ -0,0 +1,128 @@
+/* $Id$ */
+
+/***
+  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 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.
+***/
+
+/*
+  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 "base64.h"
+
+static char base64_chars[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int pos(char c)
+{
+    char *p;
+    for (p = base64_chars; *p; p++)
+        if (*p == c)
+            return p - base64_chars;
+    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 = (char *) malloc(size * 4 / 3 + 4);
+    if (p == NULL)
+        return -1;
+    q = (const unsigned char *) data;
+    i = 0;
+    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
+            val += pos(token[i]);
+    }
+    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/rtp/base64.h b/src/modules/rtp/base64.h
new file mode 100644
index 0000000..199c635
--- /dev/null
+++ b/src/modules/rtp/base64.h
@@ -0,0 +1,35 @@
+#ifndef foobase64hfoo
+#define foobase64hfoo
+
+/* $Id$ */
+
+/***
+  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 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.
+***/
+
+/*
+  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

commit 6570620cc3717eb82acd19788538fda3786c7b99
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Thu May 1 23:51:45 2008 +0000

    Start the raop sink. It's based on pipe sink and isn't anywhere near finished. It does however compile.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2335 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/Makefile.am b/src/Makefile.am
index f277198..831e456 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1004,7 +1004,13 @@ libsocket_util_la_SOURCES = \
 libsocket_util_la_LDFLAGS = -avoid-version
 libsocket_util_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) libpulsecore.la
 
-librtp_la_SOURCES = modules/rtp/rtp.c modules/rtp/rtp.h modules/rtp/sdp.c modules/rtp/sdp.h modules/rtp/sap.c modules/rtp/sap.h
+librtp_la_SOURCES = \
+		modules/rtp/rtp.c modules/rtp/rtp.h \
+		modules/rtp/sdp.c modules/rtp/sdp.h \
+		modules/rtp/sap.c modules/rtp/sap.h \
+		modules/rtp/rtsp.c modules/rtp/rtsp.h \
+		modules/rtp/headerlist.c modules/rtp/headerlist.h \
+		modules/rtp/base64.c modules/rtp/base64.h
 librtp_la_LDFLAGS = -avoid-version
 librtp_la_LIBADD = $(AM_LIBADD) libpulsecore.la
 
@@ -1053,6 +1059,7 @@ modlibexec_LTLIBRARIES += \
 		module-remap-sink.la \
 		module-ladspa-sink.la \
 		module-esound-sink.la \
+		module-raop-sink.la \
 		module-tunnel-sink.la \
 		module-tunnel-source.la \
 		module-position-event-sounds.la
@@ -1199,6 +1206,7 @@ SYMDEF_FILES = \
 		modules/module-esound-compat-spawnfd-symdef.h \
 		modules/module-esound-compat-spawnpid-symdef.h \
 		modules/module-match-symdef.h \
+		modules/module-raop-sink-symdef.h \
 		modules/module-tunnel-sink-symdef.h \
 		modules/module-tunnel-source-symdef.h \
 		modules/module-null-sink-symdef.h \
@@ -1367,6 +1375,10 @@ module_match_la_SOURCES = modules/module-match.c
 module_match_la_LDFLAGS = -module -avoid-version
 module_match_la_LIBADD = $(AM_LIBADD) libpulsecore.la
 
+module_raop_sink_la_SOURCES = modules/module-raop-sink.c
+module_raop_sink_la_LDFLAGS = -module -avoid-version
+module_raop_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la librtp.la
+
 module_tunnel_sink_la_SOURCES = modules/module-tunnel.c
 module_tunnel_sink_la_CFLAGS = -DTUNNEL_SINK=1 $(AM_CFLAGS)
 module_tunnel_sink_la_LDFLAGS = -module -avoid-version
diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
new file mode 100644
index 0000000..ad10d78
--- /dev/null
+++ b/src/modules/module-raop-sink.c
@@ -0,0 +1,417 @@
+/* $Id$ */
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  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 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 <sys/stat.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+#include <poll.h>
+#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 <pulsecore/core-error.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/random.h>
+#include <pulsecore/rtpoll.h>
+
+#include "rtp.h"
+#include "sdp.h"
+#include "sap.h"
+#include "rtsp.h"
+#include "base64.h"
+
+
+#include "module-raop-sink-symdef.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
+
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION("RAOP Sink (Apple Airport)");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+        "server=<address> "
+        "sink_name=<name for the sink> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate>"
+        "channel_map=<channel map>");
+
+#define DEFAULT_SINK_NAME "airtunes"
+#define AES_CHUNKSIZE 16
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    char *server_name;
+
+    // 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
+
+    pa_rtsp_context *rtsp;
+    //pa_socket_client *client;
+    pa_memchunk memchunk;
+
+    pa_rtpoll_item *rtpoll_item;
+};
+
+static const char* const valid_modargs[] = {
+    "server",
+    "rate",
+    "format",
+    "channels",
+    "sink_name",
+    "channel_map",
+    NULL
+};
+
+static int rsa_encrypt(uint8_t *text, int len, uint8_t *res) {
+    char n[] =
+        "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC"
+        "5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDR"
+        "KSKv6kDqnw4UwPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuB"
+        "OitnZ/bDzPHrTOZz0Dew0uowxf/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJ"
+        "Q+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/UAaHqn9JdsBWLUEpVviYnh"
+        "imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew==";
+    char e[] = "AQAB";
+    uint8_t modules[256];
+    uint8_t exponent[8];
+    int size;
+    RSA *rsa;
+
+    rsa = RSA_new();
+    size = pa_base64_decode(n, modules);
+    rsa->n = BN_bin2bn(modules, size, NULL);
+    size = pa_base64_decode(e, exponent);
+    rsa->e = BN_bin2bn(exponent, size, NULL);
+
+    size = RSA_public_encrypt(len, text, res, rsa, RSA_PKCS1_OAEP_PADDING);
+    RSA_free(rsa);
+    return size;
+}
+
+static int aes_encrypt(struct userdata *u, uint8_t *data, int size)
+{
+    uint8_t *buf;
+    int i=0, j;
+
+    pa_assert(u);
+
+    memcpy(u->aes_nv, u->aes_iv, AES_CHUNKSIZE);
+    while (i+AES_CHUNKSIZE <= size) {
+        buf = data + i;
+        for (j=0; j<AES_CHUNKSIZE; ++j)
+            buf[j] ^= u->aes_nv[j];
+
+        AES_encrypt(buf, buf, &u->aes);
+        memcpy(u->aes_nv, buf, AES_CHUNKSIZE);
+        i += AES_CHUNKSIZE;
+    }
+    return i;
+}
+
+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;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            size_t n = 0;
+            //int l;
+
+#ifdef TIOCINQ
+            /*
+            if (ioctl(u->fd, TIOCINQ, &l) >= 0 && l > 0)
+                n = (size_t) l;
+            */
+#endif
+
+            n += u->memchunk.length;
+
+            *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec);
+            break;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    //int write_type = 0;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_rtpoll_install(u->rtpoll);
+
+    for (;;) {
+        struct pollfd *pollfd;
+        int ret;
+
+        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+        /* Render some data and write it to the fifo */
+        if (u->sink->thread_info.state == PA_SINK_RUNNING && pollfd->revents) {
+            ssize_t l;
+            void *p;
+
+            if (u->memchunk.length <= 0)
+                pa_sink_render(u->sink, PIPE_BUF, &u->memchunk);
+
+            pa_assert(u->memchunk.length > 0);
+
+            p = pa_memblock_acquire(u->memchunk.memblock);
+            //l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
+            // Fake the length of the "write".
+            l = u->memchunk.length;
+            pa_memblock_release(u->memchunk.memblock);
+
+            pa_assert(l != 0);
+
+            if (l < 0) {
+
+                if (errno == EINTR)
+                    continue;
+                else if (errno != EAGAIN) {
+                    pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+                    goto fail;
+                }
+
+            } else {
+
+                u->memchunk.index += l;
+                u->memchunk.length -= l;
+
+                if (u->memchunk.length <= 0) {
+                    pa_memblock_unref(u->memchunk.memblock);
+                    pa_memchunk_reset(&u->memchunk);
+                }
+
+                pollfd->revents = 0;
+            }
+        }
+
+        /* Hmm, nothing to do. Let's sleep */
+        pollfd->events = u->sink->thread_info.state == PA_SINK_RUNNING ? POLLOUT : 0;
+
+        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+
+        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+        if (pollfd->revents & ~POLLOUT) {
+            pa_log("FIFO shutdown.");
+            goto fail;
+        }
+    }
+
+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");
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    //struct stat st;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma;
+    char *t;
+    struct pollfd *pollfd;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+
+    // Initialise the AES encryption system
+    pa_random_seed();
+    pa_random(u->aes_iv, sizeof(u->aes_iv));
+    pa_random(u->aes_key, sizeof(u->aes_key));
+    memcpy(u->aes_nv, u->aes_iv, sizeof(u->aes_nv));
+    AES_set_encrypt_key(u->aes_key, 128, &u->aes);
+
+    pa_memchunk_reset(&u->memchunk);
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+    u->rtpoll = pa_rtpoll_new();
+    pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
+
+    u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL));
+
+    // Open a connection to the server... this is just to connect and test....
+    /*
+    mkfifo(u->filename, 0666);
+    if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) {
+        pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_make_fd_cloexec(u->fd);
+    pa_make_fd_nonblock(u->fd);
+
+    if (fstat(u->fd, &st) < 0) {
+        pa_log("fstat('%s'): %s", u->filename, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (!S_ISFIFO(st.st_mode)) {
+        pa_log("'%s' is not a FIFO.", u->filename);
+        goto fail;
+    }
+    */
+    if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->userdata = u;
+    u->sink->flags = PA_SINK_LATENCY;
+
+    pa_sink_set_module(u->sink, m);
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+    pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Airtunes sink '%s'", u->server_name));
+    pa_xfree(t);
+
+    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->events = pollfd->revents = 0;
+
+    if (!(u->thread = pa_thread_new(thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    pa_sink_put(u->sink);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+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->memchunk.memblock)
+       pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    pa_xfree(u->server_name);
+    pa_xfree(u);
+}

commit 91edf9eaca3a0da83484c9c3787beff2cc7a5945
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Fri May 2 09:47:09 2008 +0000

    Use pa_sprintf_malloc to do simple concatenation rather than using the higher overhead of pa_strbuf
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2348 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/headerlist.c b/src/modules/rtp/headerlist.c
index 9ea17ae..8bdc725 100644
--- a/src/modules/rtp/headerlist.c
+++ b/src/modules/rtp/headerlist.c
@@ -92,22 +92,20 @@ int pa_headerlist_puts(pa_headerlist *p, const char *key, const char *value) {
 int pa_headerlist_putsappend(pa_headerlist *p, const char *key, const char *value) {
     struct header *hdr;
     pa_bool_t add = FALSE;
-    pa_strbuf *buf;
 
     pa_assert(p);
     pa_assert(key);
 
-    buf = pa_strbuf_new();
     if (!(hdr = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
         hdr = pa_xnew(struct header, 1);
         hdr->key = pa_xstrdup(key);
+        hdr->value = pa_xstrdup(value);
         add = TRUE;
     } else {
-        pa_strbuf_puts(buf, hdr->value);
+        void *newval = (void*)pa_sprintf_malloc("%s%s", (char*)hdr->value, value);
         pa_xfree(hdr->value);
+        hdr->value = newval;
     }
-    pa_strbuf_puts(buf, value);
-    hdr->value = pa_strbuf_tostring_free(buf);
     hdr->nbytes = strlen(hdr->value)+1;
 
     if (add)

commit ce9a41ef06e0b6c619b985415ccac6f0fddd68b8
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Fri May 2 09:49:28 2008 +0000

    Use _free rather than _destroy so as not to mix naming conventions.
    
    Convert C++ comments to C.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2349 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/rtsp.c b/src/modules/rtp/rtsp.c
index 3fd1ba0..6168871 100644
--- a/src/modules/rtp/rtsp.c
+++ b/src/modules/rtp/rtsp.c
@@ -75,12 +75,12 @@ static int pa_read_line(int fd, char *line, int maxlen, int timeout)
         if (-1 == rval) {
             if (EAGAIN == errno)
                 return 0;
-            //ERRMSG("%s:read error: %s\n", __func__, strerror(errno));
+            /*ERRMSG("%s:read error: %s\n", __func__, strerror(errno));*/
             return -1;
         }
 
         if (0 == rval) {
-            //INFMSG("%s:disconnected on the other end\n", __func__);
+            /*INFMSG("%s:disconnected on the other end\n", __func__);*/
             return -1;
         }
 
@@ -131,7 +131,7 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
     if (c->session)
         pa_strbuf_printf(buf, "Session: %s\r\n", c->session);
 
-    // Add the headers
+    /* Add the headers */
     if (headers) {
         hdrs = pa_headerlist_to_string(headers);
         pa_strbuf_puts(buf, hdrs);
@@ -157,18 +157,18 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
         pa_strbuf_puts(buf, content);
     }
 
-    // Our packet is created... now we can send it :)
+    /* Our packet is created... now we can send it :) */
     hdrs = pa_strbuf_tostring_free(buf);
     l = pa_write(c->fd, hdrs, strlen(hdrs), NULL);
     pa_xfree(hdrs);
 
-    // Do we expect a response?
+    /* Do we expect a response? */
     if (!expect_response)
         return 1;
 
     timeout = 5000;
     if (pa_read_line(c->fd, response, sizeof(response), timeout) <= 0) {
-        //ERRMSG("%s: request failed\n",__func__);
+        /*ERRMSG("%s: request failed\n",__func__);*/
         return 0;
     }
 
@@ -179,69 +179,70 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
     token = pa_split(response, delimiters, &token_state);
     if (!token || strcmp(token, "200")) {
         pa_xfree(token);
-        //ERRMSG("%s: request failed, error %s\n",__func__,token);
+        /*ERRMSG("%s: request failed, error %s\n",__func__,token);*/
         return 0;
     }
     pa_xfree(token);
 
-    // We want to return the headers?
+    /* We want to return the headers? */
     if (!response_headers)
     {
-        // We have no storage, so just clear out the response.
+        /* We have no storage, so just clear out the response. */
         while (pa_read_line(c->fd, response, sizeof(response), timeout) > 0) {
-            // Reduce timeout for future requests
+            /* Reduce timeout for future requests */
             timeout = 1000;
         }
         return 1;
     }
 
+    /* TODO: Move header reading into the headerlist. */
     header = NULL;
     buf = pa_strbuf_new();
     while (pa_read_line(c->fd, response, sizeof(response), timeout) > 0) {
-        // Reduce timeout for future requests
+        /* Reduce timeout for future requests */
         timeout = 1000;
 
-        // If the first character is a space, it's a continuation header
+        /* If the first character is a space, it's a continuation header */
         if (header && ' ' == response[0]) {
-            // Add this line to the buffer (sans the space.
+            /* Add this line to the buffer (sans the space. */
             pa_strbuf_puts(buf, &(response[1]));
             continue;
         }
 
         if (header) {
-            // This is not a continuation header so let's dump the full header/value into our proplist
+            /* This is not a continuation header so let's dump the full
+               header/value into our proplist */
             pa_headerlist_puts(*response_headers, header, pa_strbuf_tostring_free(buf));
             pa_xfree(header);
-            //header = NULL;
             buf = pa_strbuf_new();
         }
 
         delimpos = strstr(response, ":");
         if (!delimpos) {
-            //ERRMSG("%s: Request failed, bad header\n",__func__);
+            /*ERRMSG("%s: Request failed, bad header\n",__func__);*/
             return 0;
         }
 
         if (strlen(delimpos) > 1) {
-            // Cut our line off so we can copy the header name out
+            /* Cut our line off so we can copy the header name out */
             *delimpos++ = '\0';
 
-            // Trim the front of any spaces
+            /* Trim the front of any spaces */
             while (' ' == *delimpos)
                 ++delimpos;
 
             pa_strbuf_puts(buf, delimpos);
         } else {
-            // Cut our line off so we can copy the header name out
+            /* Cut our line off so we can copy the header name out */
             *delimpos = '\0';
         }
 
-        // Save the header name
+        /* Save the header name */
         header = pa_xstrdup(response);
     }
-    // We will have a header left from our looping itteration, so add it in :)
+    /* We will have a header left from our looping itteration, so add it in :) */
     if (header) {
-        // This is not a continuation header so let's dump it into our proplist
+        /* This is not a continuation header so let's dump it into our proplist */
         pa_headerlist_puts(*response_headers, header, pa_strbuf_tostring(buf));
     }
     pa_strbuf_free(buf);
@@ -266,7 +267,7 @@ pa_rtsp_context* pa_rtsp_context_new(const char* useragent) {
 }
 
 
-void pa_rtsp_context_destroy(pa_rtsp_context* c) {
+void pa_rtsp_context_free(pa_rtsp_context* c) {
     if (c) {
         pa_xfree(c->url);
         pa_xfree(c->session);
@@ -308,7 +309,7 @@ int pa_rtsp_connect(pa_rtsp_context *c, const char* hostname, uint16_t port, con
         return 0;
     }
 
-    // Q: is FD_CLOEXEC reqd?
+    /* Q: is FD_CLOEXEC reqd? */
     pa_make_fd_cloexec(c->fd);
     pa_make_tcp_socket_low_delay(c->fd);
 
@@ -395,7 +396,7 @@ int pa_rtsp_setup(pa_rtsp_context* c, pa_headerlist** response_headers) {
         return 0;
     }
 
-    // Now parse out the server port component of the response.
+    /* Now parse out the server port component of the response. */
     c->port = 0;
     delimiters[0] = ';';
     delimiters[1] = '\0';
@@ -411,7 +412,7 @@ int pa_rtsp_setup(pa_rtsp_context* c, pa_headerlist** response_headers) {
         pa_xfree(token);
     }
     if (0 == c->port) {
-        // Error no server_port in response
+        /* Error no server_port in response */
         pa_headerlist_free(rheaders);
         return 0;
     }
@@ -427,7 +428,7 @@ int pa_rtsp_record(pa_rtsp_context* c) {
 
     pa_assert(c);
     if (!c->session) {
-        // No seesion in progres
+        /* No seesion in progres */
         return 0;
     }
 
diff --git a/src/modules/rtp/rtsp.h b/src/modules/rtp/rtsp.h
index 504f144..7b3df8f 100644
--- a/src/modules/rtp/rtsp.h
+++ b/src/modules/rtp/rtsp.h
@@ -48,7 +48,7 @@ typedef struct pa_rtsp_context {
 } pa_rtsp_context;
 
 pa_rtsp_context* pa_rtsp_context_new(const char* useragent);
-void pa_rtsp_context_destroy(pa_rtsp_context* c);
+void pa_rtsp_context_free(pa_rtsp_context* c);
 
 int pa_rtsp_connect(pa_rtsp_context* c, const char* hostname, uint16_t port, const char* sid);
 void pa_rtsp_disconnect(pa_rtsp_context* c);

commit 405cf720dc5190f14eee6e2eaad51aa52ff18c62
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 4 00:43:31 2008 +0000

    Convert to using pa_socket_client rather than using blocking IO.
    
    This change requires a reference to the mainloop api be passed during initial connection.
    In addition, the passing in of the session id during connect has been deprecated. A new function pa_rtsp_set_url has been added to allow the URL to be set by external code. The concept of sid is something specific to raop, not to the rtsp client.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2360 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/rtsp.c b/src/modules/rtp/rtsp.c
index 6168871..3556230 100644
--- a/src/modules/rtp/rtsp.c
+++ b/src/modules/rtp/rtsp.c
@@ -55,12 +55,17 @@
  * if CR comes then following LF is expected
  * returned string in line is always null terminated, maxlen-1 is maximum string length
  */
-static int pa_read_line(int fd, char *line, int maxlen, int timeout)
+static int pa_read_line(pa_iochannel* io, char *line, int maxlen, int timeout)
 {
     int i, rval;
     int count;
+    int fd;
     char ch;
     struct pollfd pfds;
+
+    pa_assert(io);
+    fd = pa_iochannel_get_recv_fd(io);
+
     count = 0;
     *line = 0;
     pfds.events = POLLIN;
@@ -159,7 +164,7 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
 
     /* Our packet is created... now we can send it :) */
     hdrs = pa_strbuf_tostring_free(buf);
-    l = pa_write(c->fd, hdrs, strlen(hdrs), NULL);
+    l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));
     pa_xfree(hdrs);
 
     /* Do we expect a response? */
@@ -167,7 +172,7 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
         return 1;
 
     timeout = 5000;
-    if (pa_read_line(c->fd, response, sizeof(response), timeout) <= 0) {
+    if (pa_read_line(c->io, response, sizeof(response), timeout) <= 0) {
         /*ERRMSG("%s: request failed\n",__func__);*/
         return 0;
     }
@@ -188,7 +193,7 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
     if (!response_headers)
     {
         /* We have no storage, so just clear out the response. */
-        while (pa_read_line(c->fd, response, sizeof(response), timeout) > 0) {
+        while (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
             /* Reduce timeout for future requests */
             timeout = 1000;
         }
@@ -198,7 +203,7 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
     /* TODO: Move header reading into the headerlist. */
     header = NULL;
     buf = pa_strbuf_new();
-    while (pa_read_line(c->fd, response, sizeof(response), timeout) > 0) {
+    while (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
         /* Reduce timeout for future requests */
         timeout = 1000;
 
@@ -255,7 +260,6 @@ pa_rtsp_context* pa_rtsp_context_new(const char* useragent) {
     pa_rtsp_context *c;
 
     c = pa_xnew0(pa_rtsp_context, 1);
-    c->fd = -1;
     c->headers = pa_headerlist_new();
 
     if (useragent)
@@ -269,7 +273,11 @@ pa_rtsp_context* pa_rtsp_context_new(const char* useragent) {
 
 void pa_rtsp_context_free(pa_rtsp_context* c) {
     if (c) {
+        if (c->sc)
+            pa_socket_client_unref(c->sc);
+
         pa_xfree(c->url);
+        pa_xfree(c->localip);
         pa_xfree(c->session);
         pa_xfree(c->transport);
         pa_headerlist_free(c->headers);
@@ -278,63 +286,57 @@ void pa_rtsp_context_free(pa_rtsp_context* c) {
 }
 
 
-int pa_rtsp_connect(pa_rtsp_context *c, const char* hostname, uint16_t port, const char* sid) {
-    struct sockaddr_in sa;
-    struct sockaddr_in name;
-    socklen_t namelen = sizeof(name);
-    struct hostent *host = NULL;
-    int r;
 
+static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
+    pa_rtsp_context *c = userdata;
+    union {
+        struct sockaddr sa;
+        struct sockaddr_in in;
+        struct sockaddr_in6 in6;
+    } sa;
+    socklen_t sa_len = sizeof(sa);
+
+    pa_assert(sc);
     pa_assert(c);
-    pa_assert(hostname);
-    pa_assert(port > 0);
-    pa_assert(sid);
-
-    memset(&sa, 0, sizeof(sa));
-    sa.sin_family = AF_INET;
-    sa.sin_port = htons(port);
-
-    host = gethostbyname(hostname);
-    if (!host) {
-        unsigned int addr = inet_addr(hostname);
-        if (addr != INADDR_NONE)
-            host = gethostbyaddr((char*)&addr, 4, AF_INET);
-        if (!host)
-            return 0;
-    }
-    memcpy(&sa.sin_addr, host->h_addr, sizeof(struct in_addr));
+    pa_assert(c->sc == sc);
 
-    if ((c->fd = socket(sa.sin_family, SOCK_STREAM, 0)) < 0) {
-        pa_log("socket(): %s", pa_cstrerror(errno));
-        return 0;
-    }
+    pa_socket_client_unref(c->sc);
+    c->sc = NULL;
 
-    /* Q: is FD_CLOEXEC reqd? */
-    pa_make_fd_cloexec(c->fd);
-    pa_make_tcp_socket_low_delay(c->fd);
-
-    if ((r = connect(c->fd, &sa, sizeof(struct sockaddr_in))) < 0) {
-#ifdef OS_IS_WIN32
-        if (WSAGetLastError() != EWOULDBLOCK) {
-            pa_log_debug("connect(): %d", WSAGetLastError());
-#else
-        if (errno != EINPROGRESS) {
-            pa_log_debug("connect(): %s (%d)", pa_cstrerror(errno), errno);
-#endif
-            pa_close(c->fd);
-            c->fd = -1;
-            return 0;
+    if (!io) {
+        pa_log("Connection failed: %s", pa_cstrerror(errno));
+        return;
+    }
+    pa_assert(!c->io);
+    c->io = io;
+
+    /* Get the local IP address for use externally */
+    if (0 == getsockname(pa_iochannel_get_recv_fd(io), &sa.sa, &sa_len)) {
+        char buf[INET6_ADDRSTRLEN];
+        const char *res = NULL;
+
+        if (AF_INET == sa.sa.sa_family) {
+            res = inet_ntop(sa.sa.sa_family, &sa.in.sin_addr, buf, sizeof(buf));
+        } else if (AF_INET6 == sa.sa.sa_family) {
+            res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf));
         }
+        if (res)
+          c->localip = pa_xstrdup(res);
     }
+}
 
-    if (0 != getsockname(c->fd, (struct sockaddr*)&name, &namelen)) {
-        pa_close(c->fd);
-        c->fd = -1;
+int pa_rtsp_connect(pa_rtsp_context *c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port) {
+    pa_assert(c);
+    pa_assert(mainloop);
+    pa_assert(hostname);
+    pa_assert(port > 0);
+
+    if (!(c->sc = pa_socket_client_new_string(mainloop, hostname, port))) {
+        pa_log("failed to connect to server '%s:%d'", hostname, port);
         return 0;
     }
-    memcpy(&c->local_addr, &name.sin_addr, sizeof(struct in_addr));
-    c->url = pa_sprintf_malloc("rtsp://%s/%s", inet_ntoa(name.sin_addr), sid);
 
+    pa_socket_client_set_callback(c->sc, on_connection, c);
     return 1;
 }
 
@@ -342,22 +344,25 @@ int pa_rtsp_connect(pa_rtsp_context *c, const char* hostname, uint16_t port, con
 void pa_rtsp_disconnect(pa_rtsp_context *c) {
     pa_assert(c);
 
-    if (c->fd < 0)
-      return;
-    pa_close(c->fd);
-    c->fd = -1;
+    if (c->io)
+        pa_iochannel_free(c->io);
+    c->io = NULL;
 }
 
 
 const char* pa_rtsp_localip(pa_rtsp_context* c) {
     pa_assert(c);
 
-    if (c->fd < 0)
-        return NULL;
-    return inet_ntoa(c->local_addr);
+    return c->localip;
 }
 
 
+void pa_rtsp_set_url(pa_rtsp_context* c, const char* url) {
+    pa_assert(c);
+
+    c->url = pa_xstrdup(url);
+}
+
 int pa_rtsp_announce(pa_rtsp_context *c, const char* sdp) {
     pa_assert(c);
     if (!sdp)
diff --git a/src/modules/rtp/rtsp.h b/src/modules/rtp/rtsp.h
index 7b3df8f..8d86f7b 100644
--- a/src/modules/rtp/rtsp.h
+++ b/src/modules/rtp/rtsp.h
@@ -32,28 +32,31 @@
 #include <pulsecore/memblockq.h>
 #include <pulsecore/memchunk.h>
 #include <pulsecore/socket-client.h>
+#include <pulse/mainloop-api.h>
 
 #include "headerlist.h"
 
 typedef struct pa_rtsp_context {
-    int fd;
+    pa_socket_client *sc;
+    pa_iochannel *io;
     const char* useragent;
     pa_headerlist* headers;
+    char* localip;
     char* url;
     uint32_t port;
     uint32_t cseq;
     char* session;
     char* transport;
-    struct in_addr local_addr;
 } pa_rtsp_context;
 
 pa_rtsp_context* pa_rtsp_context_new(const char* useragent);
 void pa_rtsp_context_free(pa_rtsp_context* c);
 
-int pa_rtsp_connect(pa_rtsp_context* c, const char* hostname, uint16_t port, const char* sid);
+int pa_rtsp_connect(pa_rtsp_context* c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port);
 void pa_rtsp_disconnect(pa_rtsp_context* c);
 
 const char* pa_rtsp_localip(pa_rtsp_context* c);
+void pa_rtsp_set_url(pa_rtsp_context* c, const char* url);
 int pa_rtsp_announce(pa_rtsp_context* c, const char* sdp);
 
 int pa_rtsp_setup(pa_rtsp_context* c, pa_headerlist** response_headers);

commit 27ed970adf66ea27d3db47c2b0e138d3d7e0f0b3
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 4 01:01:52 2008 +0000

    Convert the return values to fit with the rest of pulse 0 == success, < 0 == failure
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2362 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/rtsp.c b/src/modules/rtp/rtsp.c
index 3556230..9f4d5e4 100644
--- a/src/modules/rtp/rtsp.c
+++ b/src/modules/rtp/rtsp.c
@@ -129,7 +129,7 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
     pa_assert(c->url);
 
     if (!cmd)
-        return 0;
+        return -1;
 
     buf = pa_strbuf_new();
     pa_strbuf_printf(buf, "%s %s RTSP/1.0\r\nCSeq: %d\r\n", cmd, c->url, ++c->cseq);
@@ -169,12 +169,12 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
 
     /* Do we expect a response? */
     if (!expect_response)
-        return 1;
+        return 0;
 
     timeout = 5000;
     if (pa_read_line(c->io, response, sizeof(response), timeout) <= 0) {
         /*ERRMSG("%s: request failed\n",__func__);*/
-        return 0;
+        return -1;
     }
 
     delimiters[0] = ' ';
@@ -185,7 +185,7 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
     if (!token || strcmp(token, "200")) {
         pa_xfree(token);
         /*ERRMSG("%s: request failed, error %s\n",__func__,token);*/
-        return 0;
+        return -1;
     }
     pa_xfree(token);
 
@@ -197,7 +197,7 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
             /* Reduce timeout for future requests */
             timeout = 1000;
         }
-        return 1;
+        return 0;
     }
 
     /* TODO: Move header reading into the headerlist. */
@@ -225,7 +225,7 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
         delimpos = strstr(response, ":");
         if (!delimpos) {
             /*ERRMSG("%s: Request failed, bad header\n",__func__);*/
-            return 0;
+            return -1;
         }
 
         if (strlen(delimpos) > 1) {
@@ -252,7 +252,7 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
     }
     pa_strbuf_free(buf);
 
-    return 1;
+    return 0;
 }
 
 
@@ -333,11 +333,11 @@ int pa_rtsp_connect(pa_rtsp_context *c, pa_mainloop_api *mainloop, const char* h
 
     if (!(c->sc = pa_socket_client_new_string(mainloop, hostname, port))) {
         pa_log("failed to connect to server '%s:%d'", hostname, port);
-        return 0;
+        return -1;
     }
 
     pa_socket_client_set_callback(c->sc, on_connection, c);
-    return 1;
+    return 0;
 }
 
 
@@ -366,7 +366,7 @@ void pa_rtsp_set_url(pa_rtsp_context* c, const char* url) {
 int pa_rtsp_announce(pa_rtsp_context *c, const char* sdp) {
     pa_assert(c);
     if (!sdp)
-        return 0;
+        return -1;
 
     return pa_rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL, NULL);
 }
@@ -386,10 +386,10 @@ int pa_rtsp_setup(pa_rtsp_context* c, pa_headerlist** response_headers) {
     rheaders = pa_headerlist_new();
     pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
 
-    if (!pa_rtsp_exec(c, "SETUP", NULL, NULL, 1, headers, &rheaders)) {
+    if (pa_rtsp_exec(c, "SETUP", NULL, NULL, 1, headers, &rheaders)) {
         pa_headerlist_free(headers);
         pa_headerlist_free(rheaders);
-        return 0;
+        return -1;
     }
     pa_headerlist_free(headers);
 
@@ -398,7 +398,7 @@ int pa_rtsp_setup(pa_rtsp_context* c, pa_headerlist** response_headers) {
 
     if (!c->session || !c->transport) {
         pa_headerlist_free(rheaders);
-        return 0;
+        return -1;
     }
 
     /* Now parse out the server port component of the response. */
@@ -419,11 +419,11 @@ int pa_rtsp_setup(pa_rtsp_context* c, pa_headerlist** response_headers) {
     if (0 == c->port) {
         /* Error no server_port in response */
         pa_headerlist_free(rheaders);
-        return 0;
+        return -1;
     }
 
     *response_headers = rheaders;
-    return 1;
+    return 0;
 }
 
 
@@ -434,7 +434,7 @@ int pa_rtsp_record(pa_rtsp_context* c) {
     pa_assert(c);
     if (!c->session) {
         /* No seesion in progres */
-        return 0;
+        return -1;
     }
 
     headers = pa_headerlist_new();
@@ -457,7 +457,7 @@ int pa_rtsp_teardown(pa_rtsp_context *c) {
 int pa_rtsp_setparameter(pa_rtsp_context *c, const char* param) {
     pa_assert(c);
     if (!param)
-        return 0;
+        return -1;
 
     return pa_rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL, NULL);
 }

commit a08d733fd149d3d927583bad0dc69104d08b0ceb
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 4 01:26:29 2008 +0000

    Fix svn properties and some minor indentation
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2363 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/rtsp.c b/src/modules/rtp/rtsp.c
index 9f4d5e4..d844072 100644
--- a/src/modules/rtp/rtsp.c
+++ b/src/modules/rtp/rtsp.c
@@ -321,7 +321,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
             res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf));
         }
         if (res)
-          c->localip = pa_xstrdup(res);
+            c->localip = pa_xstrdup(res);
     }
 }
 
diff --git a/src/modules/rtp/rtsp.h b/src/modules/rtp/rtsp.h
index 8d86f7b..181d085 100644
--- a/src/modules/rtp/rtsp.h
+++ b/src/modules/rtp/rtsp.h
@@ -1,7 +1,7 @@
 #ifndef foortsphfoo
 #define foortsphfoo
 
-/* $Id: rtp.h 1465 2007-05-29 17:24:48Z lennart $ */
+/* $Id$ */
 
 /***
   This file is part of PulseAudio.

commit a0d3582fb1bddbb8fb6a7da98bbfeb05b517088e
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Tue May 6 00:14:33 2008 +0000

    Trivial change to allocate memory using pulse methods.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2364 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/base64.c b/src/modules/rtp/base64.c
index ec9f221..043ef5a 100644
--- a/src/modules/rtp/base64.c
+++ b/src/modules/rtp/base64.c
@@ -33,6 +33,8 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <pulse/xmalloc.h>
+
 #include "base64.h"
 
 static char base64_chars[] =
@@ -54,9 +56,7 @@ int pa_base64_encode(const void *data, int size, char **str)
     int c;
     const unsigned char *q;
 
-    p = s = (char *) malloc(size * 4 / 3 + 4);
-    if (p == NULL)
-        return -1;
+    p = s = pa_xnew(char, size * 4 / 3 + 4);
     q = (const unsigned char *) data;
     i = 0;
     for (i = 0; i < size;) {

commit d423605bd9f5fb18b44fde5424b075c977de25ad
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Tue May 6 00:17:17 2008 +0000

    Move closer to an asynchronous structure (still some parsing code to be converted).
    
    Move type definition into .c file to keep it private
    Add more utility functions to add/remove headers and return the serverport now the structure is private.
    This commit will break the test application but I will fix that in due course
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2365 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/rtsp.c b/src/modules/rtp/rtsp.c
index d844072..55d9101 100644
--- a/src/modules/rtp/rtsp.c
+++ b/src/modules/rtp/rtsp.c
@@ -49,6 +49,22 @@
 
 #include "rtsp.h"
 
+struct pa_rtsp_context {
+    pa_socket_client *sc;
+    pa_iochannel *io;
+    pa_rtsp_cb_t callback;
+    void* userdata;
+    const char* useragent;
+    pa_headerlist* headers;
+    char* localip;
+    char* url;
+    uint32_t port;
+    uint32_t cseq;
+    char* session;
+    char* transport;
+    pa_rtsp_state state;
+};
+
 /*
  * read one line from the file descriptor
  * timeout: msec unit, -1 for infinite
@@ -112,18 +128,10 @@ static int pa_read_line(pa_iochannel* io, char *line, int maxlen, int timeout)
 static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
                         const char* content_type, const char* content,
                         int expect_response,
-                        pa_headerlist* headers, pa_headerlist** response_headers) {
+                        pa_headerlist* headers) {
     pa_strbuf* buf;
     char* hdrs;
     ssize_t l;
-    char response[1024];
-    int timeout;
-    char* token;
-    const char* token_state;
-    char delimiters[2];
-    char* header;
-    char* delimpos;
-
 
     pa_assert(c);
     pa_assert(c->url);
@@ -167,91 +175,6 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
     l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));
     pa_xfree(hdrs);
 
-    /* Do we expect a response? */
-    if (!expect_response)
-        return 0;
-
-    timeout = 5000;
-    if (pa_read_line(c->io, response, sizeof(response), timeout) <= 0) {
-        /*ERRMSG("%s: request failed\n",__func__);*/
-        return -1;
-    }
-
-    delimiters[0] = ' ';
-    delimiters[1] = '\0';
-    token_state = NULL;
-    pa_xfree(pa_split(response, delimiters, &token_state));
-    token = pa_split(response, delimiters, &token_state);
-    if (!token || strcmp(token, "200")) {
-        pa_xfree(token);
-        /*ERRMSG("%s: request failed, error %s\n",__func__,token);*/
-        return -1;
-    }
-    pa_xfree(token);
-
-    /* We want to return the headers? */
-    if (!response_headers)
-    {
-        /* We have no storage, so just clear out the response. */
-        while (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
-            /* Reduce timeout for future requests */
-            timeout = 1000;
-        }
-        return 0;
-    }
-
-    /* TODO: Move header reading into the headerlist. */
-    header = NULL;
-    buf = pa_strbuf_new();
-    while (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
-        /* Reduce timeout for future requests */
-        timeout = 1000;
-
-        /* If the first character is a space, it's a continuation header */
-        if (header && ' ' == response[0]) {
-            /* Add this line to the buffer (sans the space. */
-            pa_strbuf_puts(buf, &(response[1]));
-            continue;
-        }
-
-        if (header) {
-            /* This is not a continuation header so let's dump the full
-               header/value into our proplist */
-            pa_headerlist_puts(*response_headers, header, pa_strbuf_tostring_free(buf));
-            pa_xfree(header);
-            buf = pa_strbuf_new();
-        }
-
-        delimpos = strstr(response, ":");
-        if (!delimpos) {
-            /*ERRMSG("%s: Request failed, bad header\n",__func__);*/
-            return -1;
-        }
-
-        if (strlen(delimpos) > 1) {
-            /* Cut our line off so we can copy the header name out */
-            *delimpos++ = '\0';
-
-            /* Trim the front of any spaces */
-            while (' ' == *delimpos)
-                ++delimpos;
-
-            pa_strbuf_puts(buf, delimpos);
-        } else {
-            /* Cut our line off so we can copy the header name out */
-            *delimpos = '\0';
-        }
-
-        /* Save the header name */
-        header = pa_xstrdup(response);
-    }
-    /* We will have a header left from our looping itteration, so add it in :) */
-    if (header) {
-        /* This is not a continuation header so let's dump it into our proplist */
-        pa_headerlist_puts(*response_headers, header, pa_strbuf_tostring(buf));
-    }
-    pa_strbuf_free(buf);
-
     return 0;
 }
 
@@ -286,6 +209,146 @@ void pa_rtsp_context_free(pa_rtsp_context* c) {
 }
 
 
+static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata) {
+    pa_strbuf* buf;
+    pa_headerlist* response_headers = NULL;
+    char response[1024];
+    int timeout;
+    char* token;
+    char* header;
+    char* delimpos;
+    char delimiters[] = " ";
+    pa_rtsp_context *c = userdata;
+    pa_assert(c);
+
+    /* TODO: convert this to a pa_ioline based reader */
+    if (STATE_CONNECT == c->state) {
+        response_headers = pa_headerlist_new();
+    }
+    timeout = 5000;
+    /* read in any response headers */
+    if (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
+        const char* token_state = NULL;
+
+        timeout = 1000;
+        pa_xfree(pa_split(response, delimiters, &token_state));
+        token = pa_split(response, delimiters, &token_state);
+        if (!token || strcmp(token, "200")) {
+            pa_xfree(token);
+            pa_log("Invalid Response");
+            /* TODO: Bail out completely */
+            return;
+        }
+        pa_xfree(token);
+
+        /* We want to return the headers? */
+        if (!response_headers) {
+            /* We have no storage, so just clear out the response. */
+            while (pa_read_line(c->io, response, sizeof(response), timeout) > 0);
+        } else {
+            /* TODO: Move header reading into the headerlist. */
+            header = NULL;
+            buf = pa_strbuf_new();
+            while (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
+                /* If the first character is a space, it's a continuation header */
+                if (header && ' ' == response[0]) {
+                    /* Add this line to the buffer (sans the space. */
+                    pa_strbuf_puts(buf, &(response[1]));
+                    continue;
+                }
+
+                if (header) {
+                    /* This is not a continuation header so let's dump the full
+                      header/value into our proplist */
+                    pa_headerlist_puts(response_headers, header, pa_strbuf_tostring_free(buf));
+                    pa_xfree(header);
+                    buf = pa_strbuf_new();
+                }
+
+                delimpos = strstr(response, ":");
+                if (!delimpos) {
+                    pa_log("Invalid response header");
+                    return;
+                }
+
+                if (strlen(delimpos) > 1) {
+                    /* Cut our line off so we can copy the header name out */
+                    *delimpos++ = '\0';
+
+                    /* Trim the front of any spaces */
+                    while (' ' == *delimpos)
+                        ++delimpos;
+
+                    pa_strbuf_puts(buf, delimpos);
+                } else {
+                    /* Cut our line off so we can copy the header name out */
+                    *delimpos = '\0';
+                }
+
+                /* Save the header name */
+                header = pa_xstrdup(response);
+            }
+            /* We will have a header left from our looping itteration, so add it in :) */
+            if (header) {
+                /* This is not a continuation header so let's dump it into our proplist */
+                pa_headerlist_puts(response_headers, header, pa_strbuf_tostring(buf));
+            }
+            pa_strbuf_free(buf);
+        }
+    }
+
+    /* Deal with a CONNECT response */
+    if (STATE_CONNECT == c->state) {
+        const char* token_state = NULL;
+        const char* pc = NULL;
+        c->session = pa_xstrdup(pa_headerlist_gets(response_headers, "Session"));
+        c->transport = pa_xstrdup(pa_headerlist_gets(response_headers, "Transport"));
+
+        if (!c->session || !c->transport) {
+            pa_headerlist_free(response_headers);
+            return;
+        }
+
+        /* Now parse out the server port component of the response. */
+        c->port = 0;
+        delimiters[0] = ';';
+        while ((token = pa_split(c->transport, delimiters, &token_state))) {
+            if ((pc = strstr(token, "="))) {
+                if (0 == strncmp(token, "server_port", 11)) {
+                    pa_atou(pc+1, &c->port);
+                    pa_xfree(token);
+                    break;
+                }
+            }
+            pa_xfree(token);
+        }
+        if (0 == c->port) {
+            /* Error no server_port in response */
+            pa_headerlist_free(response_headers);
+            return;
+        }
+    }
+
+    /* Call our callback */
+    if (c->callback)
+        c->callback(c, c->state, response_headers, c->userdata);
+
+
+    if (response_headers)
+        pa_headerlist_free(response_headers);
+
+    /*
+    if (do_read(u) < 0 || do_write(u) < 0) {
+
+        if (u->io) {
+            pa_iochannel_free(u->io);
+            u->io = NULL;
+        }
+
+       pa_module_unload_request(u->module);
+    }
+    */
+}
 
 static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
     pa_rtsp_context *c = userdata;
@@ -309,6 +372,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
     }
     pa_assert(!c->io);
     c->io = io;
+    pa_iochannel_set_callback(c->io, io_callback, c);
 
     /* Get the local IP address for use externally */
     if (0 == getsockname(pa_iochannel_get_recv_fd(io), &sa.sa, &sa_len)) {
@@ -337,9 +401,16 @@ int pa_rtsp_connect(pa_rtsp_context *c, pa_mainloop_api *mainloop, const char* h
     }
 
     pa_socket_client_set_callback(c->sc, on_connection, c);
+    c->state = STATE_CONNECT;
     return 0;
 }
 
+void pa_rtsp_set_callback(pa_rtsp_context *c, pa_rtsp_cb_t callback, void *userdata) {
+    pa_assert(c);
+
+    c->callback = callback;
+    c->userdata = userdata;
+}
 
 void pa_rtsp_disconnect(pa_rtsp_context *c) {
     pa_assert(c);
@@ -356,6 +427,11 @@ const char* pa_rtsp_localip(pa_rtsp_context* c) {
     return c->localip;
 }
 
+uint32_t pa_rtsp_serverport(pa_rtsp_context* c) {
+    pa_assert(c);
+
+    return c->port;
+}
 
 void pa_rtsp_set_url(pa_rtsp_context* c, const char* url) {
     pa_assert(c);
@@ -363,67 +439,46 @@ void pa_rtsp_set_url(pa_rtsp_context* c, const char* url) {
     c->url = pa_xstrdup(url);
 }
 
+void pa_rtsp_add_header(pa_rtsp_context *c, const char* key, const char* value)
+{
+    pa_assert(c);
+    pa_assert(key);
+    pa_assert(value);
+
+    pa_headerlist_puts(c->headers, key, value);
+}
+
+void pa_rtsp_remove_header(pa_rtsp_context *c, const char* key)
+{
+    pa_assert(c);
+    pa_assert(key);
+
+    pa_headerlist_remove(c->headers, key);
+}
+
 int pa_rtsp_announce(pa_rtsp_context *c, const char* sdp) {
     pa_assert(c);
     if (!sdp)
         return -1;
 
-    return pa_rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL, NULL);
+    c->state = STATE_ANNOUNCE;
+    return pa_rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL);
 }
 
 
-int pa_rtsp_setup(pa_rtsp_context* c, pa_headerlist** response_headers) {
+int pa_rtsp_setup(pa_rtsp_context* c) {
     pa_headerlist* headers;
-    pa_headerlist* rheaders;
-    char delimiters[2];
-    char* token;
-    const char* token_state;
-    const char* pc;
+    int rv;
 
     pa_assert(c);
 
     headers = pa_headerlist_new();
-    rheaders = pa_headerlist_new();
     pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
 
-    if (pa_rtsp_exec(c, "SETUP", NULL, NULL, 1, headers, &rheaders)) {
-        pa_headerlist_free(headers);
-        pa_headerlist_free(rheaders);
-        return -1;
-    }
+    c->state = STATE_SETUP;
+    rv = pa_rtsp_exec(c, "SETUP", NULL, NULL, 1, headers);
     pa_headerlist_free(headers);
-
-    c->session = pa_xstrdup(pa_headerlist_gets(rheaders, "Session"));
-    c->transport = pa_xstrdup(pa_headerlist_gets(rheaders, "Transport"));
-
-    if (!c->session || !c->transport) {
-        pa_headerlist_free(rheaders);
-        return -1;
-    }
-
-    /* Now parse out the server port component of the response. */
-    c->port = 0;
-    delimiters[0] = ';';
-    delimiters[1] = '\0';
-    token_state = NULL;
-    while ((token = pa_split(c->transport, delimiters, &token_state))) {
-        if ((pc = strstr(token, "="))) {
-            if (0 == strncmp(token, "server_port", 11)) {
-                pa_atou(pc+1, &c->port);
-                pa_xfree(token);
-                break;
-            }
-        }
-        pa_xfree(token);
-    }
-    if (0 == c->port) {
-        /* Error no server_port in response */
-        pa_headerlist_free(rheaders);
-        return -1;
-    }
-
-    *response_headers = rheaders;
-    return 0;
+    return rv;
 }
 
 
@@ -441,7 +496,8 @@ int pa_rtsp_record(pa_rtsp_context* c) {
     pa_headerlist_puts(headers, "Range", "npt=0-");
     pa_headerlist_puts(headers, "RTP-Info", "seq=0;rtptime=0");
 
-    rv = pa_rtsp_exec(c, "RECORD", NULL, NULL, 1, headers, NULL);
+    c->state = STATE_RECORD;
+    rv = pa_rtsp_exec(c, "RECORD", NULL, NULL, 1, headers);
     pa_headerlist_free(headers);
     return rv;
 }
@@ -450,7 +506,8 @@ int pa_rtsp_record(pa_rtsp_context* c) {
 int pa_rtsp_teardown(pa_rtsp_context *c) {
     pa_assert(c);
 
-    return pa_rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL, NULL);
+    c->state = STATE_TEARDOWN;
+    return pa_rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
 }
 
 
@@ -459,7 +516,8 @@ int pa_rtsp_setparameter(pa_rtsp_context *c, const char* param) {
     if (!param)
         return -1;
 
-    return pa_rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL, NULL);
+    c->state = STATE_SET_PARAMETER;
+    return pa_rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL);
 }
 
 
@@ -472,7 +530,8 @@ int pa_rtsp_flush(pa_rtsp_context *c) {
     headers = pa_headerlist_new();
     pa_headerlist_puts(headers, "RTP-Info", "seq=0;rtptime=0");
 
-    rv = pa_rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers, NULL);
+    c->state = STATE_FLUSH;
+    rv = pa_rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers);
     pa_headerlist_free(headers);
     return rv;
 }
diff --git a/src/modules/rtp/rtsp.h b/src/modules/rtp/rtsp.h
index 181d085..6458f85 100644
--- a/src/modules/rtp/rtsp.h
+++ b/src/modules/rtp/rtsp.h
@@ -36,30 +36,35 @@
 
 #include "headerlist.h"
 
-typedef struct pa_rtsp_context {
-    pa_socket_client *sc;
-    pa_iochannel *io;
-    const char* useragent;
-    pa_headerlist* headers;
-    char* localip;
-    char* url;
-    uint32_t port;
-    uint32_t cseq;
-    char* session;
-    char* transport;
-} pa_rtsp_context;
+typedef struct pa_rtsp_context pa_rtsp_context;
+typedef enum {
+  STATE_CONNECT,
+  STATE_ANNOUNCE,
+  STATE_SETUP,
+  STATE_RECORD,
+  STATE_TEARDOWN,
+  STATE_SET_PARAMETER,
+  STATE_FLUSH
+} pa_rtsp_state;
+typedef void (*pa_rtsp_cb_t)(pa_rtsp_context *c, pa_rtsp_state state, pa_headerlist* hl, void *userdata);
 
 pa_rtsp_context* pa_rtsp_context_new(const char* useragent);
 void pa_rtsp_context_free(pa_rtsp_context* c);
 
 int pa_rtsp_connect(pa_rtsp_context* c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port);
+void pa_rtsp_set_callback(pa_rtsp_context *c, pa_rtsp_cb_t callback, void *userdata);
+
 void pa_rtsp_disconnect(pa_rtsp_context* c);
 
 const char* pa_rtsp_localip(pa_rtsp_context* c);
+uint32_t pa_rtsp_serverport(pa_rtsp_context* c);
 void pa_rtsp_set_url(pa_rtsp_context* c, const char* url);
+void pa_rtsp_add_header(pa_rtsp_context *c, const char* key, const char* value);
+void pa_rtsp_remove_header(pa_rtsp_context *c, const char* key);
+
 int pa_rtsp_announce(pa_rtsp_context* c, const char* sdp);
 
-int pa_rtsp_setup(pa_rtsp_context* c, pa_headerlist** response_headers);
+int pa_rtsp_setup(pa_rtsp_context* c);
 int pa_rtsp_record(pa_rtsp_context* c);
 int pa_rtsp_teardown(pa_rtsp_context* c);
 

commit 20478a4544e8ef622434c5af5dcb4c66269a7dd9
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Tue May 6 00:20:35 2008 +0000

    Add a skeleton raop client which builds on the rtsp client.
    
    It still requires a socket client and callback system to be added before it will be functional.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2366 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/Makefile.am b/src/Makefile.am
index 831e456..5bd6388 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1009,6 +1009,7 @@ librtp_la_SOURCES = \
 		modules/rtp/sdp.c modules/rtp/sdp.h \
 		modules/rtp/sap.c modules/rtp/sap.h \
 		modules/rtp/rtsp.c modules/rtp/rtsp.h \
+		modules/rtp/raop_client.c modules/rtp/raop_client.h \
 		modules/rtp/headerlist.c modules/rtp/headerlist.h \
 		modules/rtp/base64.c modules/rtp/base64.h
 librtp_la_LDFLAGS = -avoid-version
diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
new file mode 100644
index 0000000..18e596b
--- /dev/null
+++ b/src/modules/rtp/raop_client.c
@@ -0,0 +1,308 @@
+/* $Id$ */
+
+/***
+  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 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 <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#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 <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/random.h>
+#include <pulsecore/poll.h>
+
+#include "raop_client.h"
+#include "rtsp.h"
+#include "base64.h"
+
+#define AES_CHUNKSIZE 16
+
+#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
+
+
+struct pa_raop_client {
+    pa_rtsp_context *rtsp;
+    pa_socket_client *sc;
+    const char *host;
+    char *sid;
+
+    uint8_t jack_type;
+    uint8_t jack_status;
+
+    /* 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 */
+
+    pa_iochannel *io;
+    pa_iochannel_cb_t callback;
+    void* userdata;
+};
+
+static int rsa_encrypt(uint8_t *text, int len, uint8_t *res) {
+    char n[] =
+        "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC"
+        "5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDR"
+        "KSKv6kDqnw4UwPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuB"
+        "OitnZ/bDzPHrTOZz0Dew0uowxf/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJ"
+        "Q+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/UAaHqn9JdsBWLUEpVviYnh"
+        "imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew==";
+    char e[] = "AQAB";
+    uint8_t modules[256];
+    uint8_t exponent[8];
+    int size;
+    RSA *rsa;
+
+    rsa = RSA_new();
+    size = pa_base64_decode(n, modules);
+    rsa->n = BN_bin2bn(modules, size, NULL);
+    size = pa_base64_decode(e, exponent);
+    rsa->e = BN_bin2bn(exponent, size, 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;
+}
+
+pa_raop_client* pa_raop_client_new(void)
+{
+    pa_raop_client* c = pa_xnew0(pa_raop_client, 1);
+    return c;
+}
+
+void pa_raop_client_free(pa_raop_client* c)
+{
+    pa_assert(c);
+    pa_xfree(c);
+}
+
+static int remove_char_from_string(char *str, char rc)
+{
+  int i=0, j=0, len;
+  int num = 0;
+  len = strlen(str);
+  while (i<len) {
+      if (str[i] == rc) {
+          for (j=i; j<len; j++)
+              str[j] = str[j+1];
+          len--;
+          num++;
+      } else {
+          i++;
+      }
+  }
+  return num;
+}
+
+static void rtsp_cb(pa_rtsp_context *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;
+
+            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);
+            remove_char_from_string(key, '=');
+            pa_base64_encode(c->aes_iv, AES_CHUNKSIZE, &iv);
+            remove_char_from_string(iv, '=');
+
+            pa_random(&rand_data, sizeof(rand_data));
+            pa_base64_encode(&rand_data, AES_CHUNKSIZE, &sac);
+            remove_char_from_string(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);
+            break;
+        }
+
+        case STATE_ANNOUNCE:
+            pa_rtsp_remove_header(c->rtsp, "Apple-Challenge");
+            pa_rtsp_setup(c->rtsp);
+            break;
+
+        case STATE_SETUP: {
+            char *aj = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Jack-Status"));
+            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 (!strcmp(token, "type") && !strcmp(pc+1, "digital")) {
+                          c->jack_type = JACK_TYPE_DIGITAL;
+                      }
+                    } else {
+                        if (!strcmp(token,"connected"))
+                            c->jack_status = JACK_STATUS_CONNECTED;
+                    }
+                    pa_xfree(token);
+                }
+                pa_xfree(aj);
+                pa_rtsp_record(c->rtsp);
+            } else {
+                pa_log("Audio Jack Status missing");
+            }
+            break;
+        }
+
+        case STATE_RECORD:
+            /* Connect to the actual stream ;) */
+            /* if(raopcl_stream_connect(raopcld)) goto erexit; */
+            break;
+
+        case STATE_TEARDOWN:
+        case STATE_SET_PARAMETER:
+        case STATE_FLUSH:
+            break;
+    }
+}
+
+int pa_raop_client_connect(pa_raop_client* c, pa_mainloop_api *mainloop, const char* host)
+{
+    char *sci;
+    struct {
+        uint32_t a;
+        uint32_t b;
+        uint32_t c;
+    } rand_data;
+
+    pa_assert(c);
+    pa_assert(host);
+
+    c->host = host;
+    c->rtsp = pa_rtsp_context_new("iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
+
+    /* Initialise the AES encryption system */
+    pa_random_seed();
+    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);
+    sci = pa_sprintf_malloc("%08x%08x",rand_data.b, rand_data.c);
+    pa_rtsp_add_header(c->rtsp, "Client-Instance", sci);
+    pa_rtsp_set_callback(c->rtsp, rtsp_cb, c);
+    return pa_rtsp_connect(c->rtsp, mainloop, host, 5000);
+}
+
+void pa_raop_client_disconnect(pa_raop_client* c)
+{
+
+}
+
+void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, unsigned int count)
+{
+
+}
diff --git a/src/modules/rtp/raop_client.h b/src/modules/rtp/raop_client.h
new file mode 100644
index 0000000..499b124
--- /dev/null
+++ b/src/modules/rtp/raop_client.h
@@ -0,0 +1,40 @@
+#ifndef fooraopclientfoo
+#define fooraopclientfoo
+
+/* $Id$ */
+
+/***
+  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 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 <pulse/mainloop-api.h>
+
+typedef struct pa_raop_client pa_raop_client;
+
+pa_raop_client* pa_raop_client_new(void);
+void pa_raop_client_free(pa_raop_client* c);
+
+int pa_raop_client_connect(pa_raop_client* c, pa_mainloop_api *mainloop, const char* host);
+
+void pa_raop_client_disconnect(pa_raop_client* c);
+
+void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, unsigned int count);
+
+#endif

commit 66cf1d1f66c90b5f92d67a00ea2c1f6404453d97
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Tue May 6 00:25:37 2008 +0000

    Some minor tidyup to remove code now in raop client. Still nowhere near functional.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2367 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index ad10d78..f2ddf1c 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -36,11 +36,6 @@
 #include <limits.h>
 #include <sys/ioctl.h>
 #include <poll.h>
-#include <openssl/err.h>
-#include <openssl/rand.h>
-#include <openssl/aes.h>
-#include <openssl/rsa.h>
-#include <openssl/engine.h>
 
 #include <pulse/xmalloc.h>
 
@@ -58,8 +53,7 @@
 #include "rtp.h"
 #include "sdp.h"
 #include "sap.h"
-#include "rtsp.h"
-#include "base64.h"
+#include "raop_client.h"
 
 
 #include "module-raop-sink-symdef.h"
@@ -88,7 +82,6 @@ PA_MODULE_USAGE(
         "channel_map=<channel map>");
 
 #define DEFAULT_SINK_NAME "airtunes"
-#define AES_CHUNKSIZE 16
 
 struct userdata {
     pa_core *core;
@@ -101,13 +94,7 @@ struct userdata {
 
     char *server_name;
 
-    // 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
-
-    pa_rtsp_context *rtsp;
+    pa_raop_client *raop;
     //pa_socket_client *client;
     pa_memchunk memchunk;
 
@@ -124,51 +111,6 @@ static const char* const valid_modargs[] = {
     NULL
 };
 
-static int rsa_encrypt(uint8_t *text, int len, uint8_t *res) {
-    char n[] =
-        "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC"
-        "5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDR"
-        "KSKv6kDqnw4UwPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuB"
-        "OitnZ/bDzPHrTOZz0Dew0uowxf/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJ"
-        "Q+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/UAaHqn9JdsBWLUEpVviYnh"
-        "imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew==";
-    char e[] = "AQAB";
-    uint8_t modules[256];
-    uint8_t exponent[8];
-    int size;
-    RSA *rsa;
-
-    rsa = RSA_new();
-    size = pa_base64_decode(n, modules);
-    rsa->n = BN_bin2bn(modules, size, NULL);
-    size = pa_base64_decode(e, exponent);
-    rsa->e = BN_bin2bn(exponent, size, NULL);
-
-    size = RSA_public_encrypt(len, text, res, rsa, RSA_PKCS1_OAEP_PADDING);
-    RSA_free(rsa);
-    return size;
-}
-
-static int aes_encrypt(struct userdata *u, uint8_t *data, int size)
-{
-    uint8_t *buf;
-    int i=0, j;
-
-    pa_assert(u);
-
-    memcpy(u->aes_nv, u->aes_iv, AES_CHUNKSIZE);
-    while (i+AES_CHUNKSIZE <= size) {
-        buf = data + i;
-        for (j=0; j<AES_CHUNKSIZE; ++j)
-            buf[j] ^= u->aes_nv[j];
-
-        AES_encrypt(buf, buf, &u->aes);
-        memcpy(u->aes_nv, buf, AES_CHUNKSIZE);
-        i += AES_CHUNKSIZE;
-    }
-    return i;
-}
-
 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;
 
@@ -307,13 +249,6 @@ int pa__init(pa_module*m) {
     u->module = m;
     m->userdata = u;
 
-    // Initialise the AES encryption system
-    pa_random_seed();
-    pa_random(u->aes_iv, sizeof(u->aes_iv));
-    pa_random(u->aes_key, sizeof(u->aes_key));
-    memcpy(u->aes_nv, u->aes_iv, sizeof(u->aes_nv));
-    AES_set_encrypt_key(u->aes_key, 128, &u->aes);
-
     pa_memchunk_reset(&u->memchunk);
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
     u->rtpoll = pa_rtpoll_new();

commit 8fb58e3a9082bafc3aa7b874d2bba9258b29cb38
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Tue May 6 00:28:04 2008 +0000

    Add a function for packing bits into a byte buffer. This will be needed when encoding the audio data in ALAC format.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2368 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index 18e596b..fbcbe4b 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -92,6 +92,58 @@ struct pa_raop_client {
     void* userdata;
 };
 
+/**
+ * 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 definatly 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 incremeneted 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 int rsa_encrypt(uint8_t *text, int len, uint8_t *res) {
     char n[] =
         "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC"

commit 22e299ad3e16d1a2636653a7be9d625ecdc23802
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Tue May 6 18:39:09 2008 +0000

    Add a pa_iochannel callback for when the RAOP connection connects.
    
    Properly handle the sequence of events that establish a connection.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2369 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index fbcbe4b..8f6f259 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -73,10 +73,10 @@
 
 
 struct pa_raop_client {
-    pa_rtsp_context *rtsp;
-    pa_socket_client *sc;
+    pa_mainloop_api *mainloop;
     const char *host;
     char *sid;
+    pa_rtsp_context *rtsp;
 
     uint8_t jack_type;
     uint8_t jack_status;
@@ -87,9 +87,13 @@ struct pa_raop_client {
     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;
     pa_iochannel *io;
     pa_iochannel_cb_t callback;
     void* userdata;
+
+    uint8_t *buffer;
+    /*pa_memchunk memchunk;*/
 };
 
 /**
@@ -219,6 +223,25 @@ static int remove_char_from_string(char *str, char rc)
   return num;
 }
 
+static void 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_socket_client_unref(c->sc);
+    c->sc = NULL;
+
+    if (!io) {
+        pa_log("Connection failed: %s", pa_cstrerror(errno));
+        return;
+    }
+    pa_assert(!c->io);
+    c->io = io;
+    pa_iochannel_set_callback(c->io, c->callback, c->userdata);
+}
+
 static void rtsp_cb(pa_rtsp_context *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *userdata)
 {
     pa_raop_client* c = userdata;
@@ -235,6 +258,7 @@ static void rtsp_cb(pa_rtsp_context *rtsp, pa_rtsp_state state, pa_headerlist* h
             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);
@@ -273,12 +297,14 @@ static void rtsp_cb(pa_rtsp_context *rtsp, pa_rtsp_state state, pa_headerlist* h
         }
 
         case STATE_ANNOUNCE:
+            pa_log_debug("RAOP: ANNOUNCED");
             pa_rtsp_remove_header(c->rtsp, "Apple-Challenge");
             pa_rtsp_setup(c->rtsp);
             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[] = ";";
@@ -299,17 +325,24 @@ static void rtsp_cb(pa_rtsp_context *rtsp, pa_rtsp_state state, pa_headerlist* h
                     pa_xfree(token);
                 }
                 pa_xfree(aj);
-                pa_rtsp_record(c->rtsp);
             } else {
-                pa_log("Audio Jack Status missing");
+                pa_log_warn("Audio Jack Status missing");
             }
+            pa_rtsp_record(c->rtsp);
             break;
         }
 
-        case STATE_RECORD:
-            /* Connect to the actual stream ;) */
-            /* if(raopcl_stream_connect(raopcld)) goto erexit; */
+        case STATE_RECORD: {
+            uint32_t port = pa_rtsp_serverport(c->rtsp);
+            pa_log_debug("RAOP: RECORDED");
+
+            if (!(c->sc = pa_socket_client_new_string(c->mainloop, 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);
             break;
+        }
 
         case STATE_TEARDOWN:
         case STATE_SET_PARAMETER:
@@ -330,6 +363,7 @@ int pa_raop_client_connect(pa_raop_client* c, pa_mainloop_api *mainloop, const c
     pa_assert(c);
     pa_assert(host);
 
+    c->mainloop = mainloop;
     c->host = host;
     c->rtsp = pa_rtsp_context_new("iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
 
@@ -356,5 +390,40 @@ void pa_raop_client_disconnect(pa_raop_client* c)
 
 void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, unsigned int count)
 {
+    ssize_t l;
+    uint16_t len;
+    static uint8_t header[] = {
+        0x24, 0x00, 0x00, 0x00,
+        0xF0, 0xFF, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+    };
+    const int header_size = sizeof(header);
+
+    pa_assert(c);
+    pa_assert(buffer);
+    pa_assert(count > 0);
+
+    c->buffer = pa_xrealloc(c->buffer, (count + header_size + 16));
+    memcpy(c->buffer, header, header_size);
+    len = count + header_size - 4;
+
+    /* store the lenght (endian swapped: make this better) */
+    *(c->buffer + 2) = len >> 8;
+    *(c->buffer + 3) = len & 0xff;
+
+    memcpy((c->buffer+header_size), buffer, count);
+    aes_encrypt(c, (c->buffer + header_size), count);
+    len = header_size + count;
+
+    /* TODO: move this into a memchunk/memblock and write only in callback */
+    l = pa_iochannel_write(c->io, c->buffer, len);
+}
+
+void pa_raop_client_set_callback(pa_raop_client* c, pa_iochannel_cb_t callback, void *userdata)
+{
+    pa_assert(c);
 
+    c->callback = callback;
+    c->userdata = userdata;
 }
diff --git a/src/modules/rtp/raop_client.h b/src/modules/rtp/raop_client.h
index 499b124..99c75fd 100644
--- a/src/modules/rtp/raop_client.h
+++ b/src/modules/rtp/raop_client.h
@@ -25,6 +25,7 @@
 ***/
 
 #include <pulse/mainloop-api.h>
+#include <pulsecore/iochannel.h>
 
 typedef struct pa_raop_client pa_raop_client;
 
@@ -37,4 +38,6 @@ void pa_raop_client_disconnect(pa_raop_client* c);
 
 void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, unsigned int count);
 
+void pa_raop_client_set_callback(pa_raop_client* c, pa_iochannel_cb_t callback, void *userdata);
+
 #endif
diff --git a/src/modules/rtp/rtsp.c b/src/modules/rtp/rtsp.c
index 55d9101..4f2411a 100644
--- a/src/modules/rtp/rtsp.c
+++ b/src/modules/rtp/rtsp.c
@@ -172,6 +172,8 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
 
     /* Our packet is created... now we can send it :) */
     hdrs = pa_strbuf_tostring_free(buf);
+    pa_log_debug("Submitting request:");
+    pa_log_debug(hdrs);
     l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));
     pa_xfree(hdrs);
 
@@ -220,15 +222,22 @@ static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata) {
     char delimiters[] = " ";
     pa_rtsp_context *c = userdata;
     pa_assert(c);
+    pa_assert(c->io == io);
+
+    if (!pa_iochannel_is_readable(c->io)) {
+        if (STATE_SETUP == c->state || STATE_ANNOUNCE == c->state) return;
+        goto do_callback;
+    }
 
     /* TODO: convert this to a pa_ioline based reader */
-    if (STATE_CONNECT == c->state) {
+    if (STATE_SETUP == c->state || STATE_ANNOUNCE == c->state) {
         response_headers = pa_headerlist_new();
     }
     timeout = 5000;
     /* read in any response headers */
     if (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
         const char* token_state = NULL;
+        pa_log_debug("Response Line: %s", response);
 
         timeout = 1000;
         pa_xfree(pa_split(response, delimiters, &token_state));
@@ -244,12 +253,15 @@ static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata) {
         /* We want to return the headers? */
         if (!response_headers) {
             /* We have no storage, so just clear out the response. */
-            while (pa_read_line(c->io, response, sizeof(response), timeout) > 0);
+            while (pa_read_line(c->io, response, sizeof(response), timeout) > 0){
+                pa_log_debug("Response Line: %s", response);
+            }
         } else {
             /* TODO: Move header reading into the headerlist. */
             header = NULL;
             buf = pa_strbuf_new();
             while (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
+                pa_log_debug("Response Line: %s", response);
                 /* If the first character is a space, it's a continuation header */
                 if (header && ' ' == response[0]) {
                     /* Add this line to the buffer (sans the space. */
@@ -297,8 +309,8 @@ static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata) {
         }
     }
 
-    /* Deal with a CONNECT response */
-    if (STATE_CONNECT == c->state) {
+    /* Deal with a SETUP response */
+    if (STATE_SETUP == c->state) {
         const char* token_state = NULL;
         const char* pc = NULL;
         c->session = pa_xstrdup(pa_headerlist_gets(response_headers, "Session"));
@@ -330,6 +342,7 @@ static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata) {
     }
 
     /* Call our callback */
+do_callback:
     if (c->callback)
         c->callback(c, c->state, response_headers, c->userdata);
 
@@ -387,6 +400,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
         if (res)
             c->localip = pa_xstrdup(res);
     }
+    pa_log_debug("Established RTSP connection from local ip %s", c->localip);
 }
 
 int pa_rtsp_connect(pa_rtsp_context *c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port) {

commit 6510d97315b9bdf7b1afc204c3dca0a2b0a3a528
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Wed May 7 00:35:10 2008 +0000

    Use a more stateful response parser.
    
    This makes things fully asyncronous.
    Some of the continuation headerlist stuff could be moved to headerlist for neatness, but this is OK for now.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2373 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/rtsp.c b/src/modules/rtp/rtsp.c
index 4f2411a..44cd80b 100644
--- a/src/modules/rtp/rtsp.c
+++ b/src/modules/rtp/rtsp.c
@@ -46,84 +46,35 @@
 #include <pulsecore/macro.h>
 #include <pulsecore/strbuf.h>
 #include <pulsecore/poll.h>
+#include <pulsecore/ioline.h>
 
 #include "rtsp.h"
 
 struct pa_rtsp_context {
     pa_socket_client *sc;
     pa_iochannel *io;
-    pa_rtsp_cb_t callback;
-    void* userdata;
-    const char* useragent;
-    pa_headerlist* headers;
-    char* localip;
-    char* url;
-    uint32_t port;
-    uint32_t cseq;
-    char* session;
-    char* transport;
-    pa_rtsp_state state;
-};
+    pa_ioline *ioline;
 
-/*
- * read one line from the file descriptor
- * timeout: msec unit, -1 for infinite
- * if CR comes then following LF is expected
- * returned string in line is always null terminated, maxlen-1 is maximum string length
- */
-static int pa_read_line(pa_iochannel* io, char *line, int maxlen, int timeout)
-{
-    int i, rval;
-    int count;
-    int fd;
-    char ch;
-    struct pollfd pfds;
-
-    pa_assert(io);
-    fd = pa_iochannel_get_recv_fd(io);
-
-    count = 0;
-    *line = 0;
-    pfds.events = POLLIN;
-    pfds.fd = fd;
-
-    for (i=0; i<maxlen; ++i) {
-        if (!poll(&pfds, 1, timeout))
-            return 0;
-
-        rval = read(fd, &ch, 1);
-
-        if (-1 == rval) {
-            if (EAGAIN == errno)
-                return 0;
-            /*ERRMSG("%s:read error: %s\n", __func__, strerror(errno));*/
-            return -1;
-        }
-
-        if (0 == rval) {
-            /*INFMSG("%s:disconnected on the other end\n", __func__);*/
-            return -1;
-        }
-
-        if ('\n' == ch) {
-            *line = 0;
-            return count;
-        }
-
-        if ('\r' == ch)
-            continue;
+    pa_rtsp_cb_t callback;
 
-        *line++ = ch;
-        count++;
+    void *userdata;
+    const char *useragent;
 
-        if (count >= maxlen-1)
-            break;
-    }
+    pa_rtsp_state state;
+    uint8_t waiting;
 
-    *line = 0;
-    return count;
-}
+    pa_headerlist* headers;
+    char *last_header;
+    pa_strbuf *header_buffer;
+    pa_headerlist* response_headers;
 
+    char *localip;
+    char *url;
+    uint32_t port;
+    uint32_t cseq;
+    char *session;
+    char *transport;
+};
 
 static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
                         const char* content_type, const char* content,
@@ -172,8 +123,8 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
 
     /* Our packet is created... now we can send it :) */
     hdrs = pa_strbuf_tostring_free(buf);
-    pa_log_debug("Submitting request:");
-    pa_log_debug(hdrs);
+    /*pa_log_debug("Submitting request:");
+    pa_log_debug(hdrs);*/
     l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));
     pa_xfree(hdrs);
 
@@ -205,125 +156,39 @@ void pa_rtsp_context_free(pa_rtsp_context* c) {
         pa_xfree(c->localip);
         pa_xfree(c->session);
         pa_xfree(c->transport);
+        pa_xfree(c->last_header);
+        if (c->header_buffer)
+            pa_strbuf_free(c->header_buffer);
+        if (c->response_headers)
+            pa_headerlist_free(c->response_headers);
         pa_headerlist_free(c->headers);
     }
     pa_xfree(c);
 }
 
 
-static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata) {
-    pa_strbuf* buf;
-    pa_headerlist* response_headers = NULL;
-    char response[1024];
-    int timeout;
+static void headers_read(pa_rtsp_context *c) {
     char* token;
-    char* header;
-    char* delimpos;
-    char delimiters[] = " ";
-    pa_rtsp_context *c = userdata;
-    pa_assert(c);
-    pa_assert(c->io == io);
-
-    if (!pa_iochannel_is_readable(c->io)) {
-        if (STATE_SETUP == c->state || STATE_ANNOUNCE == c->state) return;
-        goto do_callback;
-    }
-
-    /* TODO: convert this to a pa_ioline based reader */
-    if (STATE_SETUP == c->state || STATE_ANNOUNCE == c->state) {
-        response_headers = pa_headerlist_new();
-    }
-    timeout = 5000;
-    /* read in any response headers */
-    if (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
-        const char* token_state = NULL;
-        pa_log_debug("Response Line: %s", response);
-
-        timeout = 1000;
-        pa_xfree(pa_split(response, delimiters, &token_state));
-        token = pa_split(response, delimiters, &token_state);
-        if (!token || strcmp(token, "200")) {
-            pa_xfree(token);
-            pa_log("Invalid Response");
-            /* TODO: Bail out completely */
-            return;
-        }
-        pa_xfree(token);
-
-        /* We want to return the headers? */
-        if (!response_headers) {
-            /* We have no storage, so just clear out the response. */
-            while (pa_read_line(c->io, response, sizeof(response), timeout) > 0){
-                pa_log_debug("Response Line: %s", response);
-            }
-        } else {
-            /* TODO: Move header reading into the headerlist. */
-            header = NULL;
-            buf = pa_strbuf_new();
-            while (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
-                pa_log_debug("Response Line: %s", response);
-                /* If the first character is a space, it's a continuation header */
-                if (header && ' ' == response[0]) {
-                    /* Add this line to the buffer (sans the space. */
-                    pa_strbuf_puts(buf, &(response[1]));
-                    continue;
-                }
-
-                if (header) {
-                    /* This is not a continuation header so let's dump the full
-                      header/value into our proplist */
-                    pa_headerlist_puts(response_headers, header, pa_strbuf_tostring_free(buf));
-                    pa_xfree(header);
-                    buf = pa_strbuf_new();
-                }
-
-                delimpos = strstr(response, ":");
-                if (!delimpos) {
-                    pa_log("Invalid response header");
-                    return;
-                }
-
-                if (strlen(delimpos) > 1) {
-                    /* Cut our line off so we can copy the header name out */
-                    *delimpos++ = '\0';
-
-                    /* Trim the front of any spaces */
-                    while (' ' == *delimpos)
-                        ++delimpos;
+    char delimiters[] = ";";
 
-                    pa_strbuf_puts(buf, delimpos);
-                } else {
-                    /* Cut our line off so we can copy the header name out */
-                    *delimpos = '\0';
-                }
-
-                /* Save the header name */
-                header = pa_xstrdup(response);
-            }
-            /* We will have a header left from our looping itteration, so add it in :) */
-            if (header) {
-                /* This is not a continuation header so let's dump it into our proplist */
-                pa_headerlist_puts(response_headers, header, pa_strbuf_tostring(buf));
-            }
-            pa_strbuf_free(buf);
-        }
-    }
+    pa_assert(c);
+    pa_assert(c->response_headers);
 
     /* Deal with a SETUP response */
     if (STATE_SETUP == c->state) {
         const char* token_state = NULL;
         const char* pc = NULL;
-        c->session = pa_xstrdup(pa_headerlist_gets(response_headers, "Session"));
-        c->transport = pa_xstrdup(pa_headerlist_gets(response_headers, "Transport"));
+        c->session = pa_xstrdup(pa_headerlist_gets(c->response_headers, "Session"));
+        c->transport = pa_xstrdup(pa_headerlist_gets(c->response_headers, "Transport"));
 
         if (!c->session || !c->transport) {
-            pa_headerlist_free(response_headers);
+            pa_headerlist_free(c->response_headers);
+            c->response_headers = NULL;
+            pa_log("Invalid SETUP response.");
             return;
         }
 
         /* Now parse out the server port component of the response. */
-        c->port = 0;
-        delimiters[0] = ';';
         while ((token = pa_split(c->transport, delimiters, &token_state))) {
             if ((pc = strstr(token, "="))) {
                 if (0 == strncmp(token, "server_port", 11)) {
@@ -336,33 +201,117 @@ static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata) {
         }
         if (0 == c->port) {
             /* Error no server_port in response */
-            pa_headerlist_free(response_headers);
+            pa_headerlist_free(c->response_headers);
+            c->response_headers = NULL;
+            pa_log("Invalid SETUP response (no port number).");
             return;
         }
     }
 
     /* Call our callback */
-do_callback:
     if (c->callback)
-        c->callback(c, c->state, response_headers, c->userdata);
+        c->callback(c, c->state, c->response_headers, c->userdata);
 
+    pa_headerlist_free(c->response_headers);
+    c->response_headers = NULL;
+}
 
-    if (response_headers)
-        pa_headerlist_free(response_headers);
 
-    /*
-    if (do_read(u) < 0 || do_write(u) < 0) {
+static void line_callback(pa_ioline *line, const char *s, void *userdata) {
+    char *delimpos;
+    char *s2, *s2p;
 
-        if (u->io) {
-            pa_iochannel_free(u->io);
-            u->io = NULL;
+    pa_rtsp_context *c = userdata;
+    pa_assert(line);
+    pa_assert(c);
+    pa_assert(s);
+
+    s2 = pa_xstrdup(s);
+    /* Trim trailing carriage returns */
+    s2p = s2 + strlen(s2) - 1;
+    while (s2p >= s2 && '\r' == *s2p) {
+        *s2p = '\0';
+        s2p -= 1;
+    }
+    if (c->waiting && 0 == strcmp("RTSP/1.0 200 OK", s2)) {
+        c->waiting = 0;
+        pa_assert(!c->response_headers);
+        c->response_headers = pa_headerlist_new();
+        goto exit;
+    }
+    if (c->waiting) {
+        pa_log_warn("Unexpected response: %s", s2);
+        goto exit;;
+    }
+    if (!strlen(s2)) {
+        /* End of headers */
+        /* We will have a header left from our looping itteration, so add it in :) */
+        if (c->last_header) {
+            /* This is not a continuation header so let's dump it into our proplist */
+            pa_headerlist_puts(c->response_headers, c->last_header, pa_strbuf_tostring_free(c->header_buffer));
+            pa_xfree(c->last_header);
+            c->last_header = NULL;
+            c->header_buffer= NULL;
         }
 
-       pa_module_unload_request(u->module);
+        pa_log_debug("Full response received. Dispatching");
+        headers_read(c);
+        c->waiting = 1;
+        goto exit;
     }
-    */
+
+    /* Read and parse a header (we know it's not empty) */
+    /* TODO: Move header reading into the headerlist. */
+
+    /* If the first character is a space, it's a continuation header */
+    if (c->last_header && ' ' == s2[0]) {
+        pa_assert(c->header_buffer);
+
+        /* Add this line to the buffer (sans the space. */
+        pa_strbuf_puts(c->header_buffer, &(s2[1]));
+        goto exit;
+    }
+
+    if (c->last_header) {
+        /* This is not a continuation header so let's dump the full
+          header/value into our proplist */
+        pa_headerlist_puts(c->response_headers, c->last_header, pa_strbuf_tostring_free(c->header_buffer));
+        pa_xfree(c->last_header);
+        c->last_header = NULL;
+        c->header_buffer = NULL;
+    }
+
+    delimpos = strstr(s2, ":");
+    if (!delimpos) {
+        pa_log_warn("Unexpected response when expecting header: %s", s);
+        goto exit;
+    }
+
+    pa_assert(!c->header_buffer);
+    pa_assert(!c->last_header);
+
+    c->header_buffer = pa_strbuf_new();
+    if (strlen(delimpos) > 1) {
+        /* Cut our line off so we can copy the header name out */
+        *delimpos++ = '\0';
+
+        /* Trim the front of any spaces */
+        while (' ' == *delimpos)
+            ++delimpos;
+
+        pa_strbuf_puts(c->header_buffer, delimpos);
+    } else {
+        /* Cut our line off so we can copy the header name out */
+        *delimpos = '\0';
+    }
+
+    /* Save the header name */
+    c->last_header = pa_xstrdup(s2);
+  exit:
+    pa_xfree(s2);
 }
 
+
 static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
     pa_rtsp_context *c = userdata;
     union {
@@ -385,7 +334,9 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
     }
     pa_assert(!c->io);
     c->io = io;
-    pa_iochannel_set_callback(c->io, io_callback, c);
+
+    c->ioline = pa_ioline_new(io);
+    pa_ioline_set_callback(c->ioline, line_callback, c);
 
     /* Get the local IP address for use externally */
     if (0 == getsockname(pa_iochannel_get_recv_fd(io), &sa.sa, &sa_len)) {
@@ -401,6 +352,11 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
             c->localip = pa_xstrdup(res);
     }
     pa_log_debug("Established RTSP connection from local ip %s", c->localip);
+
+    c->waiting = 1;
+    c->state = STATE_CONNECT;
+    if (c->callback)
+        c->callback(c, c->state, NULL, c->userdata);
 }
 
 int pa_rtsp_connect(pa_rtsp_context *c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port) {

commit e596f42f39232e4e0d36c3764474f73a7ff48fbb
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Wed May 7 01:10:31 2008 +0000

    Wrap the io_callback to ensure that all data is written before asking for more.
    
    Fix the length type for send_sample (restrict to 16bit value)
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2374 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index 8f6f259..2dd2de9 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -93,6 +93,8 @@ struct pa_raop_client {
     void* userdata;
 
     uint8_t *buffer;
+    uint8_t *buffer_index;
+    uint16_t buffer_count;
     /*pa_memchunk memchunk;*/
 };
 
@@ -205,22 +207,51 @@ void pa_raop_client_free(pa_raop_client* c)
     pa_xfree(c);
 }
 
-static int remove_char_from_string(char *str, char rc)
+static inline void rtrimchar(char *str, char rc)
 {
-  int i=0, j=0, len;
-  int num = 0;
-  len = strlen(str);
-  while (i<len) {
-      if (str[i] == rc) {
-          for (j=i; j<len; j++)
-              str[j] = str[j+1];
-          len--;
-          num++;
-      } else {
-          i++;
-      }
-  }
-  return num;
+    char *sp = str + strlen(str) - 1;
+    while (sp >= str && *sp == rc) {
+        *sp = '\0';
+        sp -= 1;
+    }
+}
+
+static int pa_raop_client_process(pa_raop_client* c)
+{
+    ssize_t l;
+
+    pa_assert(c);
+
+    if (!c->buffer_index || !c->buffer_count)
+        return 1;
+
+    if (!pa_iochannel_is_writable(c->io))
+        return 0;
+    l = pa_iochannel_write(c->io, c->buffer_index, c->buffer_count);
+    /*pa_log_debug("Wrote %d bytes (from buffer)", (int)l);*/
+    if (l == c->buffer_count) {
+        c->buffer_index = NULL;
+        c->buffer_count = 0;
+        return 1;
+    }
+    c->buffer_index += l;
+    c->buffer_count -= l;
+    /*pa_log_debug("Sill have %d bytes (in buffer)", c->buffer_count);*/
+
+    return 0;
+}
+
+static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata)
+{
+    pa_raop_client *c = userdata;
+
+    pa_assert(c);
+    pa_assert(c->io == io);
+    pa_assert(c->callback);
+
+    if (pa_raop_client_process(c)) {
+        c->callback(c->io, c->userdata);
+    }
 }
 
 static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
@@ -239,7 +270,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
     }
     pa_assert(!c->io);
     c->io = io;
-    pa_iochannel_set_callback(c->io, c->callback, c->userdata);
+    pa_iochannel_set_callback(c->io, io_callback, c);
 }
 
 static void rtsp_cb(pa_rtsp_context *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *userdata)
@@ -268,13 +299,13 @@ static void rtsp_cb(pa_rtsp_context *rtsp, pa_rtsp_state state, pa_headerlist* h
             /* 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);
-            remove_char_from_string(key, '=');
+            rtrimchar(key, '=');
             pa_base64_encode(c->aes_iv, AES_CHUNKSIZE, &iv);
-            remove_char_from_string(iv, '=');
+            rtrimchar(iv, '=');
 
             pa_random(&rand_data, sizeof(rand_data));
             pa_base64_encode(&rand_data, AES_CHUNKSIZE, &sac);
-            remove_char_from_string(sac, '=');
+            rtrimchar(sac, '=');
             pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
             sdp = pa_sprintf_malloc(
                 "v=0\r\n"
@@ -388,7 +419,7 @@ void pa_raop_client_disconnect(pa_raop_client* c)
 
 }
 
-void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, unsigned int count)
+void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, uint16_t count)
 {
     ssize_t l;
     uint16_t len;
@@ -406,7 +437,7 @@ void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, unsign
 
     c->buffer = pa_xrealloc(c->buffer, (count + header_size + 16));
     memcpy(c->buffer, header, header_size);
-    len = count + header_size - 4;
+    len = header_size + count - 4;
 
     /* store the lenght (endian swapped: make this better) */
     *(c->buffer + 2) = len >> 8;
@@ -417,9 +448,18 @@ void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, unsign
     len = header_size + count;
 
     /* TODO: move this into a memchunk/memblock and write only in callback */
+    /*pa_log_debug("Channel status: %d", pa_iochannel_is_writable(c->io));
+    pa_log_debug("Writing %d bytes", len);*/
     l = pa_iochannel_write(c->io, c->buffer, len);
+    /*pa_log_debug("Wrote %d bytes", (int)l);*/
+    if (l != len) {
+        c->buffer_index = c->buffer + l;
+        c->buffer_count = len - l;
+    }
+    /*pa_log_debug("Sill have %d bytes (in buffer)", c->buffer_count);*/
 }
 
+
 void pa_raop_client_set_callback(pa_raop_client* c, pa_iochannel_cb_t callback, void *userdata)
 {
     pa_assert(c);
diff --git a/src/modules/rtp/raop_client.h b/src/modules/rtp/raop_client.h
index 99c75fd..1dcf779 100644
--- a/src/modules/rtp/raop_client.h
+++ b/src/modules/rtp/raop_client.h
@@ -36,7 +36,7 @@ int pa_raop_client_connect(pa_raop_client* c, pa_mainloop_api *mainloop, const c
 
 void pa_raop_client_disconnect(pa_raop_client* c);
 
-void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, unsigned int count);
+void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, uint16_t count);
 
 void pa_raop_client_set_callback(pa_raop_client* c, pa_iochannel_cb_t callback, void *userdata);
 

commit 41e31ab204ca48ea749c416eb270ebfa2f74b086
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Wed May 7 01:23:16 2008 +0000

    Rename rtsp.{c,h} to rtsp_client.{c,h}.
    
    Renate pa_rtsp_context to pa_rtsp_client.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2376 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/Makefile.am b/src/Makefile.am
index 5bd6388..9197920 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1008,7 +1008,7 @@ librtp_la_SOURCES = \
 		modules/rtp/rtp.c modules/rtp/rtp.h \
 		modules/rtp/sdp.c modules/rtp/sdp.h \
 		modules/rtp/sap.c modules/rtp/sap.h \
-		modules/rtp/rtsp.c modules/rtp/rtsp.h \
+		modules/rtp/rtsp_client.c modules/rtp/rtsp_client.h \
 		modules/rtp/raop_client.c modules/rtp/raop_client.h \
 		modules/rtp/headerlist.c modules/rtp/headerlist.h \
 		modules/rtp/base64.c modules/rtp/base64.h
diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index 2dd2de9..7bfce93 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -56,7 +56,7 @@
 #include <pulsecore/poll.h>
 
 #include "raop_client.h"
-#include "rtsp.h"
+#include "rtsp_client.h"
 #include "base64.h"
 
 #define AES_CHUNKSIZE 16
@@ -76,7 +76,7 @@ struct pa_raop_client {
     pa_mainloop_api *mainloop;
     const char *host;
     char *sid;
-    pa_rtsp_context *rtsp;
+    pa_rtsp_client *rtsp;
 
     uint8_t jack_type;
     uint8_t jack_status;
@@ -273,7 +273,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
     pa_iochannel_set_callback(c->io, io_callback, c);
 }
 
-static void rtsp_cb(pa_rtsp_context *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *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);
@@ -396,7 +396,7 @@ int pa_raop_client_connect(pa_raop_client* c, pa_mainloop_api *mainloop, const c
 
     c->mainloop = mainloop;
     c->host = host;
-    c->rtsp = pa_rtsp_context_new("iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
+    c->rtsp = pa_rtsp_client_new("iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
 
     /* Initialise the AES encryption system */
     pa_random_seed();
diff --git a/src/modules/rtp/rtsp.c b/src/modules/rtp/rtsp_client.c
similarity index 90%
rename from src/modules/rtp/rtsp.c
rename to src/modules/rtp/rtsp_client.c
index 44cd80b..c22f801 100644
--- a/src/modules/rtp/rtsp.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -48,9 +48,9 @@
 #include <pulsecore/poll.h>
 #include <pulsecore/ioline.h>
 
-#include "rtsp.h"
+#include "rtsp_client.h"
 
-struct pa_rtsp_context {
+struct pa_rtsp_client {
     pa_socket_client *sc;
     pa_iochannel *io;
     pa_ioline *ioline;
@@ -76,7 +76,7 @@ struct pa_rtsp_context {
     char *transport;
 };
 
-static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
+static int pa_rtsp_exec(pa_rtsp_client* c, const char* cmd,
                         const char* content_type, const char* content,
                         int expect_response,
                         pa_headerlist* headers) {
@@ -132,10 +132,10 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
 }
 
 
-pa_rtsp_context* pa_rtsp_context_new(const char* useragent) {
-    pa_rtsp_context *c;
+pa_rtsp_client* pa_rtsp_client_new(const char* useragent) {
+    pa_rtsp_client *c;
 
-    c = pa_xnew0(pa_rtsp_context, 1);
+    c = pa_xnew0(pa_rtsp_client, 1);
     c->headers = pa_headerlist_new();
 
     if (useragent)
@@ -147,7 +147,7 @@ pa_rtsp_context* pa_rtsp_context_new(const char* useragent) {
 }
 
 
-void pa_rtsp_context_free(pa_rtsp_context* c) {
+void pa_rtsp_client_free(pa_rtsp_client* c) {
     if (c) {
         if (c->sc)
             pa_socket_client_unref(c->sc);
@@ -167,7 +167,7 @@ void pa_rtsp_context_free(pa_rtsp_context* c) {
 }
 
 
-static void headers_read(pa_rtsp_context *c) {
+static void headers_read(pa_rtsp_client *c) {
     char* token;
     char delimiters[] = ";";
 
@@ -221,7 +221,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
     char *delimpos;
     char *s2, *s2p;
 
-    pa_rtsp_context *c = userdata;
+    pa_rtsp_client *c = userdata;
     pa_assert(line);
     pa_assert(c);
     pa_assert(s);
@@ -313,7 +313,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
 
 
 static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
-    pa_rtsp_context *c = userdata;
+    pa_rtsp_client *c = userdata;
     union {
         struct sockaddr sa;
         struct sockaddr_in in;
@@ -359,7 +359,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
         c->callback(c, c->state, NULL, c->userdata);
 }
 
-int pa_rtsp_connect(pa_rtsp_context *c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port) {
+int pa_rtsp_connect(pa_rtsp_client *c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port) {
     pa_assert(c);
     pa_assert(mainloop);
     pa_assert(hostname);
@@ -375,14 +375,14 @@ int pa_rtsp_connect(pa_rtsp_context *c, pa_mainloop_api *mainloop, const char* h
     return 0;
 }
 
-void pa_rtsp_set_callback(pa_rtsp_context *c, pa_rtsp_cb_t callback, void *userdata) {
+void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userdata) {
     pa_assert(c);
 
     c->callback = callback;
     c->userdata = userdata;
 }
 
-void pa_rtsp_disconnect(pa_rtsp_context *c) {
+void pa_rtsp_disconnect(pa_rtsp_client *c) {
     pa_assert(c);
 
     if (c->io)
@@ -391,25 +391,25 @@ void pa_rtsp_disconnect(pa_rtsp_context *c) {
 }
 
 
-const char* pa_rtsp_localip(pa_rtsp_context* c) {
+const char* pa_rtsp_localip(pa_rtsp_client* c) {
     pa_assert(c);
 
     return c->localip;
 }
 
-uint32_t pa_rtsp_serverport(pa_rtsp_context* c) {
+uint32_t pa_rtsp_serverport(pa_rtsp_client* c) {
     pa_assert(c);
 
     return c->port;
 }
 
-void pa_rtsp_set_url(pa_rtsp_context* 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_context *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);
@@ -418,7 +418,7 @@ void pa_rtsp_add_header(pa_rtsp_context *c, const char* key, const char* value)
     pa_headerlist_puts(c->headers, key, value);
 }
 
-void pa_rtsp_remove_header(pa_rtsp_context *c, const char* key)
+void pa_rtsp_remove_header(pa_rtsp_client *c, const char* key)
 {
     pa_assert(c);
     pa_assert(key);
@@ -426,7 +426,7 @@ void pa_rtsp_remove_header(pa_rtsp_context *c, const char* key)
     pa_headerlist_remove(c->headers, key);
 }
 
-int pa_rtsp_announce(pa_rtsp_context *c, const char* sdp) {
+int pa_rtsp_announce(pa_rtsp_client *c, const char* sdp) {
     pa_assert(c);
     if (!sdp)
         return -1;
@@ -436,7 +436,7 @@ int pa_rtsp_announce(pa_rtsp_context *c, const char* sdp) {
 }
 
 
-int pa_rtsp_setup(pa_rtsp_context* c) {
+int pa_rtsp_setup(pa_rtsp_client* c) {
     pa_headerlist* headers;
     int rv;
 
@@ -452,7 +452,7 @@ int pa_rtsp_setup(pa_rtsp_context* c) {
 }
 
 
-int pa_rtsp_record(pa_rtsp_context* c) {
+int pa_rtsp_record(pa_rtsp_client* c) {
     pa_headerlist* headers;
     int rv;
 
@@ -473,7 +473,7 @@ int pa_rtsp_record(pa_rtsp_context* c) {
 }
 
 
-int pa_rtsp_teardown(pa_rtsp_context *c) {
+int pa_rtsp_teardown(pa_rtsp_client *c) {
     pa_assert(c);
 
     c->state = STATE_TEARDOWN;
@@ -481,7 +481,7 @@ int pa_rtsp_teardown(pa_rtsp_context *c) {
 }
 
 
-int pa_rtsp_setparameter(pa_rtsp_context *c, const char* param) {
+int pa_rtsp_setparameter(pa_rtsp_client *c, const char* param) {
     pa_assert(c);
     if (!param)
         return -1;
@@ -491,7 +491,7 @@ int pa_rtsp_setparameter(pa_rtsp_context *c, const char* param) {
 }
 
 
-int pa_rtsp_flush(pa_rtsp_context *c) {
+int pa_rtsp_flush(pa_rtsp_client *c) {
     pa_headerlist* headers;
     int rv;
 
diff --git a/src/modules/rtp/rtsp.h b/src/modules/rtp/rtsp_client.h
similarity index 51%
rename from src/modules/rtp/rtsp.h
rename to src/modules/rtp/rtsp_client.h
index 6458f85..0f1daab 100644
--- a/src/modules/rtp/rtsp.h
+++ b/src/modules/rtp/rtsp_client.h
@@ -1,5 +1,5 @@
-#ifndef foortsphfoo
-#define foortsphfoo
+#ifndef foortspclienthfoo
+#define foortspclienthfoo
 
 /* $Id$ */
 
@@ -36,7 +36,7 @@
 
 #include "headerlist.h"
 
-typedef struct pa_rtsp_context pa_rtsp_context;
+typedef struct pa_rtsp_client pa_rtsp_client;
 typedef enum {
   STATE_CONNECT,
   STATE_ANNOUNCE,
@@ -46,29 +46,29 @@ typedef enum {
   STATE_SET_PARAMETER,
   STATE_FLUSH
 } pa_rtsp_state;
-typedef void (*pa_rtsp_cb_t)(pa_rtsp_context *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_context* pa_rtsp_context_new(const char* useragent);
-void pa_rtsp_context_free(pa_rtsp_context* c);
+pa_rtsp_client* pa_rtsp_client_new(const char* useragent);
+void pa_rtsp_client_free(pa_rtsp_client* c);
 
-int pa_rtsp_connect(pa_rtsp_context* c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port);
-void pa_rtsp_set_callback(pa_rtsp_context *c, pa_rtsp_cb_t callback, void *userdata);
+int pa_rtsp_connect(pa_rtsp_client* c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port);
+void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userdata);
 
-void pa_rtsp_disconnect(pa_rtsp_context* c);
+void pa_rtsp_disconnect(pa_rtsp_client* c);
 
-const char* pa_rtsp_localip(pa_rtsp_context* c);
-uint32_t pa_rtsp_serverport(pa_rtsp_context* c);
-void pa_rtsp_set_url(pa_rtsp_context* c, const char* url);
-void pa_rtsp_add_header(pa_rtsp_context *c, const char* key, const char* value);
-void pa_rtsp_remove_header(pa_rtsp_context *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_context* c, const char* sdp);
+int pa_rtsp_announce(pa_rtsp_client* c, const char* sdp);
 
-int pa_rtsp_setup(pa_rtsp_context* c);
-int pa_rtsp_record(pa_rtsp_context* c);
-int pa_rtsp_teardown(pa_rtsp_context* c);
+int pa_rtsp_setup(pa_rtsp_client* c);
+int pa_rtsp_record(pa_rtsp_client* c);
+int pa_rtsp_teardown(pa_rtsp_client* c);
 
-int pa_rtsp_setparameter(pa_rtsp_context* c, const char* param);
-int pa_rtsp_flush(pa_rtsp_context* c);
+int pa_rtsp_setparameter(pa_rtsp_client* c, const char* param);
+int pa_rtsp_flush(pa_rtsp_client* c);
 
 #endif

commit 1fb046536a687e7c5eef9a440f66d111cd0e8cb4
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sat May 10 23:01:37 2008 +0000

    Combine pa_raop_client_new and pa_raop_client_connect (no point in having them separate)
    
    Convert the iochannel to an fd and do not call a pa_iochannel_cb_t callback but rather trigger the callback on connection and pass the fd.
    Change pa_raop_client_send_sample to pa_raop_client_encode_sample and work with memchunks.
    Fix a subtle size bug in the bit writer that techincally isn't triggered in normal operation.
    Clean up the _free function to actually free stuff.
    Do the actual ALAC encoding.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2394 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index 7bfce93..0df80e1 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -88,8 +88,8 @@ struct pa_raop_client {
     uint8_t aes_key[AES_CHUNKSIZE]; /* key for aes-cbc */
 
     pa_socket_client *sc;
-    pa_iochannel *io;
-    pa_iochannel_cb_t callback;
+    int fd;
+    pa_raop_client_cb_t callback;
     void* userdata;
 
     uint8_t *buffer;
@@ -115,7 +115,7 @@ static inline void bit_writer(uint8_t **buffer, uint8_t *bit_pos, int *size, uin
 
     /* If bit pos is zero, we will definatly use at least one bit from the current byte so size increments. */
     if (!*bit_pos)
-        *size = 1;
+        *size += 1;
 
     /* Calc the number of bits left in the current byte of buffer */
     bits_left = 7 - *bit_pos  + 1;
@@ -195,18 +195,6 @@ static int aes_encrypt(pa_raop_client* c, uint8_t *data, int size)
     return i;
 }
 
-pa_raop_client* pa_raop_client_new(void)
-{
-    pa_raop_client* c = pa_xnew0(pa_raop_client, 1);
-    return c;
-}
-
-void pa_raop_client_free(pa_raop_client* c)
-{
-    pa_assert(c);
-    pa_xfree(c);
-}
-
 static inline void rtrimchar(char *str, char rc)
 {
     char *sp = str + strlen(str) - 1;
@@ -216,50 +204,14 @@ static inline void rtrimchar(char *str, char rc)
     }
 }
 
-static int pa_raop_client_process(pa_raop_client* c)
-{
-    ssize_t l;
-
-    pa_assert(c);
-
-    if (!c->buffer_index || !c->buffer_count)
-        return 1;
-
-    if (!pa_iochannel_is_writable(c->io))
-        return 0;
-    l = pa_iochannel_write(c->io, c->buffer_index, c->buffer_count);
-    /*pa_log_debug("Wrote %d bytes (from buffer)", (int)l);*/
-    if (l == c->buffer_count) {
-        c->buffer_index = NULL;
-        c->buffer_count = 0;
-        return 1;
-    }
-    c->buffer_index += l;
-    c->buffer_count -= l;
-    /*pa_log_debug("Sill have %d bytes (in buffer)", c->buffer_count);*/
-
-    return 0;
-}
-
-static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata)
-{
-    pa_raop_client *c = userdata;
-
-    pa_assert(c);
-    pa_assert(c->io == io);
-    pa_assert(c->callback);
-
-    if (pa_raop_client_process(c)) {
-        c->callback(c->io, c->userdata);
-    }
-}
-
 static void 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_socket_client_unref(c->sc);
     c->sc = NULL;
@@ -268,9 +220,16 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
         pa_log("Connection failed: %s", pa_cstrerror(errno));
         return;
     }
-    pa_assert(!c->io);
-    c->io = io;
-    pa_iochannel_set_callback(c->io, io_callback, c);
+
+    c->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_log_debug("Connection established");
+    c->callback(c->fd, c->userdata);
 }
 
 static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *userdata)
@@ -382,7 +341,7 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* he
     }
 }
 
-int pa_raop_client_connect(pa_raop_client* c, pa_mainloop_api *mainloop, const char* host)
+pa_raop_client* pa_raop_client_new(pa_mainloop_api *mainloop, const char* host)
 {
     char *sci;
     struct {
@@ -390,11 +349,12 @@ int pa_raop_client_connect(pa_raop_client* c, pa_mainloop_api *mainloop, const c
         uint32_t b;
         uint32_t c;
     } rand_data;
+    pa_raop_client* c = pa_xnew0(pa_raop_client, 1);
 
-    pa_assert(c);
     pa_assert(host);
 
     c->mainloop = mainloop;
+    c->fd = -1;
     c->host = host;
     c->rtsp = pa_rtsp_client_new("iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
 
@@ -411,18 +371,41 @@ int pa_raop_client_connect(pa_raop_client* c, pa_mainloop_api *mainloop, const c
     sci = pa_sprintf_malloc("%08x%08x",rand_data.b, rand_data.c);
     pa_rtsp_add_header(c->rtsp, "Client-Instance", sci);
     pa_rtsp_set_callback(c->rtsp, rtsp_cb, c);
-    return pa_rtsp_connect(c->rtsp, mainloop, host, 5000);
+    if (pa_rtsp_connect(c->rtsp, mainloop, host, 5000)) {
+        pa_rtsp_client_free(c->rtsp);
+        pa_xfree(c->aes_iv);
+        pa_xfree(c->aes_nv);
+        pa_xfree(c->aes_key);
+        return NULL;
+    }
+    return c;
 }
 
-void pa_raop_client_disconnect(pa_raop_client* c)
+
+void pa_raop_client_free(pa_raop_client* c)
 {
+    pa_assert(c);
 
+    pa_rtsp_client_free(c->rtsp);
+    pa_xfree(c->aes_iv);
+    pa_xfree(c->aes_nv);
+    pa_xfree(c->aes_key);
+    pa_xfree(c);
 }
 
-void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, uint16_t count)
+
+static void noop(PA_GCC_UNUSED void* p) {}
+
+pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_mempool* mempool, pa_memchunk* raw)
 {
-    ssize_t l;
-    uint16_t len;
+    uint16_t len, bufmax;
+    uint8_t *bp, bpos;
+    uint8_t *ibp, *maxibp;
+    int size;
+    uint8_t *p;
+    uint16_t bsize;
+    pa_memchunk rv;
+    size_t length;
     static uint8_t header[] = {
         0x24, 0x00, 0x00, 0x00,
         0xF0, 0xFF, 0x00, 0x00,
@@ -432,35 +415,66 @@ void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, uint16
     const int header_size = sizeof(header);
 
     pa_assert(c);
-    pa_assert(buffer);
-    pa_assert(count > 0);
-
-    c->buffer = pa_xrealloc(c->buffer, (count + header_size + 16));
+    pa_assert(c->fd > 0);
+    pa_assert(raw);
+    pa_assert(raw->memblock);
+    pa_assert(raw->length > 0);
+
+    /* 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;
+    c->buffer = pa_xrealloc(c->buffer, bufmax);
     memcpy(c->buffer, header, header_size);
-    len = header_size + count - 4;
+    pa_memchunk_reset(&rv);
+    rv.memblock = pa_memblock_new_user(mempool, c->buffer, (header_size + length), noop, 1);
+
+    /* Now write the actual samples */
+    bp = c->buffer + 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);
+
+    ibp = p = pa_memblock_acquire(raw->memblock);
+    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;
+    }
+    pa_memblock_release(raw->memblock);
+    rv.length = header_size + size;
 
     /* store the lenght (endian swapped: make this better) */
+    len = size + header_size - 4;
     *(c->buffer + 2) = len >> 8;
     *(c->buffer + 3) = len & 0xff;
 
-    memcpy((c->buffer+header_size), buffer, count);
-    aes_encrypt(c, (c->buffer + header_size), count);
-    len = header_size + count;
-
-    /* TODO: move this into a memchunk/memblock and write only in callback */
-    /*pa_log_debug("Channel status: %d", pa_iochannel_is_writable(c->io));
-    pa_log_debug("Writing %d bytes", len);*/
-    l = pa_iochannel_write(c->io, c->buffer, len);
-    /*pa_log_debug("Wrote %d bytes", (int)l);*/
-    if (l != len) {
-        c->buffer_index = c->buffer + l;
-        c->buffer_count = len - l;
-    }
-    /*pa_log_debug("Sill have %d bytes (in buffer)", c->buffer_count);*/
+    /* encrypt our data */
+    aes_encrypt(c, (c->buffer + header_size), size);
+    return rv;
 }
 
 
-void pa_raop_client_set_callback(pa_raop_client* c, pa_iochannel_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);
 
diff --git a/src/modules/rtp/raop_client.h b/src/modules/rtp/raop_client.h
index 1dcf779..68a1cdb 100644
--- a/src/modules/rtp/raop_client.h
+++ b/src/modules/rtp/raop_client.h
@@ -26,18 +26,16 @@
 
 #include <pulse/mainloop-api.h>
 #include <pulsecore/iochannel.h>
+#include <pulsecore/memchunk.h>
 
 typedef struct pa_raop_client pa_raop_client;
 
-pa_raop_client* pa_raop_client_new(void);
+pa_raop_client* pa_raop_client_new(pa_mainloop_api *mainloop, const char* host);
 void pa_raop_client_free(pa_raop_client* c);
 
-int pa_raop_client_connect(pa_raop_client* c, pa_mainloop_api *mainloop, const char* host);
+pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_mempool* mempool, pa_memchunk* raw);
 
-void pa_raop_client_disconnect(pa_raop_client* c);
-
-void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, uint16_t count);
-
-void pa_raop_client_set_callback(pa_raop_client* c, pa_iochannel_cb_t callback, void *userdata);
+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);
 
 #endif

commit f97c5debcc9564d368e6d79606df7b3ce6269d58
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 12:18:36 2008 +0000

    Properly duplicate the hostname passed in on connect.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2396 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index 0df80e1..bad747b 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -355,7 +355,7 @@ pa_raop_client* pa_raop_client_new(pa_mainloop_api *mainloop, const char* host)
 
     c->mainloop = mainloop;
     c->fd = -1;
-    c->host = host;
+    c->host = pa_xstrdup(host);
     c->rtsp = pa_rtsp_client_new("iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
 
     /* Initialise the AES encryption system */
@@ -390,6 +390,7 @@ void pa_raop_client_free(pa_raop_client* c)
     pa_xfree(c->aes_iv);
     pa_xfree(c->aes_nv);
     pa_xfree(c->aes_key);
+    pa_xfree(c->host);
     pa_xfree(c);
 }
 

commit 264a1c2ffc3b38fd88421c7d7ad9ebbab6b7e1bb
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 12:20:14 2008 +0000

    Add more libraries to librtp now that it's doing a lot more.
    
    This currently hacks in -lssl rather than writing a configure hook to detect it as I want to replace this with nss before official release.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2397 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/Makefile.am b/src/Makefile.am
index 9197920..5a9b902 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1013,7 +1013,7 @@ librtp_la_SOURCES = \
 		modules/rtp/headerlist.c modules/rtp/headerlist.h \
 		modules/rtp/base64.c modules/rtp/base64.h
 librtp_la_LDFLAGS = -avoid-version
-librtp_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+librtp_la_LIBADD = $(AM_LIBADD) libsocket-util.la libiochannel.la libsocket-client.la libioline.la libpulsecore.la -lssl
 
 # X11
 

commit d51f5944b7248ec759ab71b0e811ec0f7c655e22
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 12:21:32 2008 +0000

    A very rough first version of the sink.
    
    I can actually play music to my airport now (woot).
    Still very rough round the edges and I need to handle disconnects etc. but it's all good progress :)
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2398 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index f2ddf1c..3d08cb8 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -3,8 +3,7 @@
 /***
   This file is part of PulseAudio.
 
-  Copyright 2004-2006 Lennart Poettering
-  Copyright 2008      Colin Guthrie
+  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
@@ -34,52 +33,50 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <limits.h>
-#include <sys/ioctl.h>
 #include <poll.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/xmalloc.h>
+#include <pulse/timeval.h>
 
 #include <pulsecore/core-error.h>
+#include <pulsecore/iochannel.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
-#include <pulsecore/thread.h>
+#include <pulsecore/socket-client.h>
+#include <pulsecore/authkey.h>
 #include <pulsecore/thread-mq.h>
-#include <pulsecore/random.h>
-#include <pulsecore/rtpoll.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/socket-util.h>
 
+#include "module-raop-sink-symdef.h"
 #include "rtp.h"
 #include "sdp.h"
 #include "sap.h"
 #include "raop_client.h"
 
-
-#include "module-raop-sink-symdef.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
-
-
 PA_MODULE_AUTHOR("Colin Guthrie");
-PA_MODULE_DESCRIPTION("RAOP Sink (Apple Airport)");
+PA_MODULE_DESCRIPTION("RAOP Sink (Apple Airtunes)");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(FALSE);
 PA_MODULE_USAGE(
-        "server=<address> "
         "sink_name=<name for the sink> "
+        "server=<address> cookie=<filename>  "
         "format=<sample format> "
         "channels=<number of channels> "
-        "rate=<sample rate>"
-        "channel_map=<channel map>");
+        "rate=<sample rate>");
 
 #define DEFAULT_SINK_NAME "airtunes"
 
@@ -88,17 +85,35 @@ struct userdata {
     pa_module *module;
     pa_sink *sink;
 
-    pa_thread *thread;
     pa_thread_mq thread_mq;
     pa_rtpoll *rtpoll;
+    pa_rtpoll_item *rtpoll_item;
+    pa_thread *thread;
+
+    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;
+    int fd;
 
-    char *server_name;
+    int64_t offset;
+    int64_t encoding_overhead;
+    double encoding_ratio;
 
     pa_raop_client *raop;
-    //pa_socket_client *client;
-    pa_memchunk memchunk;
 
-    pa_rtpoll_item *rtpoll_item;
+    size_t block_size;
 };
 
 static const char* const valid_modargs[] = {
@@ -107,31 +122,65 @@ static const char* const valid_modargs[] = {
     "format",
     "channels",
     "sink_name",
-    "channel_map",
     NULL
 };
 
+enum {
+    SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX
+};
+
 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;
 
     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_OPENED(u->sink->thread_info.state));
+
+                    pa_smoother_pause(u->smoother, pa_rtclock_usec());
+                    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_usec());
+
+                    break;
+
+                case PA_SINK_UNLINKED:
+                case PA_SINK_INIT:
+                    ;
+            }
+
+            break;
+
         case PA_SINK_MESSAGE_GET_LATENCY: {
-            size_t n = 0;
-            //int l;
-
-#ifdef TIOCINQ
-            /*
-            if (ioctl(u->fd, TIOCINQ, &l) >= 0 && l > 0)
-                n = (size_t) l;
-            */
-#endif
+            pa_usec_t w, r;
 
-            n += u->memchunk.length;
+            r = pa_smoother_get(u->smoother, pa_rtclock_usec());
+            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) = pa_bytes_to_usec(n, &u->sink->sample_spec);
+            *((pa_usec_t*) data) = w > r ? w - r : 0;
             break;
         }
+
+        case SINK_MESSAGE_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->events = pollfd->revents = 0;
+
+            return 0;
+        }
     }
 
     return pa_sink_process_msg(o, code, data, offset, chunk);
@@ -139,7 +188,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
 static void thread_func(void *userdata) {
     struct userdata *u = userdata;
-    //int write_type = 0;
+    int write_type = 0;
 
     pa_assert(u);
 
@@ -148,55 +197,113 @@ static void thread_func(void *userdata) {
     pa_thread_mq_install(&u->thread_mq);
     pa_rtpoll_install(u->rtpoll);
 
+    pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+
     for (;;) {
-        struct pollfd *pollfd;
         int ret;
 
-        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
-
-        /* Render some data and write it to the fifo */
-        if (u->sink->thread_info.state == PA_SINK_RUNNING && pollfd->revents) {
-            ssize_t l;
-            void *p;
-
-            if (u->memchunk.length <= 0)
-                pa_sink_render(u->sink, PIPE_BUF, &u->memchunk);
-
-            pa_assert(u->memchunk.length > 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_OPENED(u->sink->thread_info.state) && pollfd->revents) {
+                pa_usec_t usec;
+                int64_t n;
+
+                for (;;) {
+                    ssize_t l;
+                    void *p;
+
+                    if (u->raw_memchunk.length <= 0) {
+                        /* Grab unencoded data */
+                        pa_sink_render(u->sink, u->block_size, &u->raw_memchunk);
+                    }
+                    pa_assert(u->raw_memchunk.length > 0);
+
+                    if (u->encoded_memchunk.length <= 0) {
+                        /* Encode it */
+                        size_t rl = u->raw_memchunk.length;
+                        if (u->encoded_memchunk.memblock)
+                            pa_memblock_unref(u->encoded_memchunk.memblock);
+                        u->encoded_memchunk = pa_raop_client_encode_sample(u->raop, u->core->mempool, &u->raw_memchunk);
+                        u->encoding_overhead += (u->encoded_memchunk.length - (rl - u->raw_memchunk.length));
+                        u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length);
+                    }
+                    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);
+                    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;
+
+                        if (u->encoded_memchunk.length <= 0) {
+                            pa_memblock_unref(u->encoded_memchunk.memblock);
+                            pa_memchunk_reset(&u->encoded_memchunk);
+                        }
+
+                        pollfd->revents = 0;
+
+                        if (u->encoded_memchunk.length > 0)
+
+                            /* OK, we wrote less that we asked for,
+                             * hence we can assume that the socket
+                             * buffers are full now */
+                            goto filled_up;
+                    }
+                }
 
-            p = pa_memblock_acquire(u->memchunk.memblock);
-            //l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
-            // Fake the length of the "write".
-            l = u->memchunk.length;
-            pa_memblock_release(u->memchunk.memblock);
+            filled_up:
 
-            pa_assert(l != 0);
+                /* 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 */
 
-            if (l < 0) {
+                n = u->offset;
 
-                if (errno == EINTR)
-                    continue;
-                else if (errno != EAGAIN) {
-                    pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
-                    goto fail;
+#ifdef SIOCOUTQ
+                {
+                    int l;
+                    if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
+                        n -= l;
                 }
+#endif
 
-            } else {
-
-                u->memchunk.index += l;
-                u->memchunk.length -= l;
+                usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
 
-                if (u->memchunk.length <= 0) {
-                    pa_memblock_unref(u->memchunk.memblock);
-                    pa_memchunk_reset(&u->memchunk);
-                }
+                if (usec > u->latency)
+                    usec -= u->latency;
+                else
+                    usec = 0;
 
-                pollfd->revents = 0;
+                pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
             }
-        }
 
-        /* Hmm, nothing to do. Let's sleep */
-        pollfd->events = u->sink->thread_info.state == PA_SINK_RUNNING ? POLLOUT : 0;
+            /* Hmm, nothing to do. Let's sleep */
+            pollfd->events = PA_SINK_OPENED(u->sink->thread_info.state)  ? POLLOUT : 0;
+        }
 
         if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
             goto fail;
@@ -204,11 +311,15 @@ static void thread_func(void *userdata) {
         if (ret == 0)
             goto finish;
 
-        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+        if (u->rtpoll_item) {
+            struct pollfd* pollfd;
 
-        if (pollfd->revents & ~POLLOUT) {
-            pa_log("FIFO shutdown.");
-            goto fail;
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+            if (pollfd->revents & ~POLLOUT) {
+                pa_log("FIFO shutdown.");
+                goto fail;
+            }
         }
     }
 
@@ -222,25 +333,41 @@ finish:
     pa_log_debug("Thread shutting down");
 }
 
+static void on_connection(PA_GCC_UNUSED int fd, void*userdata) {
+    struct userdata *u = userdata;
+    pa_assert(u);
+
+    pa_assert(u->fd < 0);
+    u->fd = fd;
+
+    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);
+}
+
 int pa__init(pa_module*m) {
-    struct userdata *u;
-    //struct stat st;
+    struct userdata *u = NULL;
+    const char *p;
     pa_sample_spec ss;
-    pa_channel_map map;
-    pa_modargs *ma;
+    pa_modargs *ma = NULL;
     char *t;
-    struct pollfd *pollfd;
 
     pa_assert(m);
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log("Failed to parse module arguments.");
+        pa_log("failed to parse module arguments");
         goto fail;
     }
 
     ss = m->core->default_sample_spec;
-    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
-        pa_log("Invalid sample format specification or channel map");
+    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+        pa_log("invalid sample format specification");
+        goto fail;
+    }
+
+    if ((/*ss.format != PA_SAMPLE_U8 &&*/ ss.format != PA_SAMPLE_S16NE) ||
+        (ss.channels > 2)) {
+        pa_log("sample type support is limited to mono/stereo and U8 or S16NE sample data");
         goto fail;
     }
 
@@ -248,54 +375,58 @@ int pa__init(pa_module*m) {
     u->core = m->core;
     u->module = m;
     m->userdata = u;
+    u->fd = -1;
+    u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE);
+    pa_memchunk_reset(&u->raw_memchunk);
+    pa_memchunk_reset(&u->encoded_memchunk);
+    u->offset = 0;
+    u->encoding_overhead = 0;
+    u->encoding_ratio = 1.0;
 
-    pa_memchunk_reset(&u->memchunk);
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
     u->rtpoll = pa_rtpoll_new();
     pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
+    u->rtpoll_item = NULL;
 
-    u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", 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);
 
-    // Open a connection to the server... this is just to connect and test....
-    /*
-    mkfifo(u->filename, 0666);
-    if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) {
-        pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
-        goto fail;
-    }
-
-    pa_make_fd_cloexec(u->fd);
-    pa_make_fd_nonblock(u->fd);
+    u->read_data = u->write_data = NULL;
+    u->read_index = u->write_index = u->read_length = u->write_length = 0;
 
-    if (fstat(u->fd, &st) < 0) {
-        pa_log("fstat('%s'): %s", u->filename, pa_cstrerror(errno));
-        goto fail;
-    }
+    /*u->state = STATE_AUTH;*/
+    u->latency = 0;
 
-    if (!S_ISFIFO(st.st_mode)) {
-        pa_log("'%s' is not a FIFO.", u->filename);
-        goto fail;
-    }
-    */
-    if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
+    if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) {
         pa_log("Failed to create sink.");
         goto fail;
     }
 
     u->sink->parent.process_msg = sink_process_msg;
     u->sink->userdata = u;
-    u->sink->flags = PA_SINK_LATENCY;
+    u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK;
 
     pa_sink_set_module(u->sink, m);
     pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
     pa_sink_set_rtpoll(u->sink, u->rtpoll);
-    pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Airtunes sink '%s'", u->server_name));
+
+    if (!(p = pa_modargs_get_value(ma, "server", NULL))) {
+        pa_log("No server argument given.");
+        goto fail;
+    }
+
+    if (!(u->raop = pa_raop_client_new(u->core->mainloop, p))) {
+        pa_log("Failed to connect to server.");
+        goto fail;
+    }
+
+    pa_raop_client_set_callback(u->raop, on_connection, u);
+    pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Airtunes sink '%s'", p));
     pa_xfree(t);
 
-    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->events = pollfd->revents = 0;
 
     if (!(u->thread = pa_thread_new(thread_func, u))) {
         pa_log("Failed to create thread.");
@@ -319,7 +450,6 @@ fail:
 
 void pa__done(pa_module*m) {
     struct userdata *u;
-
     pa_assert(m);
 
     if (!(u = m->userdata))
@@ -338,15 +468,29 @@ void pa__done(pa_module*m) {
     if (u->sink)
         pa_sink_unref(u->sink);
 
-    if (u->memchunk.memblock)
-       pa_memblock_unref(u->memchunk.memblock);
-
     if (u->rtpoll_item)
         pa_rtpoll_item_free(u->rtpoll_item);
 
     if (u->rtpoll)
         pa_rtpoll_free(u->rtpoll);
 
-    pa_xfree(u->server_name);
+    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);
+
+    if (u->fd >= 0)
+        pa_close(u->fd);
+
     pa_xfree(u);
 }

commit 4dd318519fbec1811a16dca05aca859da74b60c2
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 13:32:09 2008 +0000

    Do not assert on NULL values of s. This means the connection was closed. This change somehow kills the mainloop with an assert, so I need to sort that out.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2399 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index c22f801..5665c9f 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -224,7 +224,14 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
     pa_rtsp_client *c = userdata;
     pa_assert(line);
     pa_assert(c);
-    pa_assert(s);
+
+    if (!s) {
+        pa_log_warn("Connection closed");
+        pa_ioline_unref(c->ioline);
+        c->ioline = NULL;
+        pa_rtsp_disconnect(c);
+        return;
+    }
 
     s2 = pa_xstrdup(s);
     /* Trim trailing carriage returns */

commit 5eecfa2e3f3abcacc9df2776cba798598e5fb6ee
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 13:35:01 2008 +0000

    Move the ownership of the encoded data memchunk into the raop_client.
    
    This does not seem to fix the pool full messages so I'll have to try and suss that out.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2400 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 3d08cb8..f6f93a4 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -1,4 +1,4 @@
-/* $Id$ */
+/* $Id: module-esound-sink.c 2043 2007-11-09 18:25:40Z lennart $ */
 
 /***
   This file is part of PulseAudio.
@@ -109,6 +109,7 @@ struct userdata {
 
     int64_t offset;
     int64_t encoding_overhead;
+    int32_t next_encoding_overhead;
     double encoding_ratio;
 
     pa_raop_client *raop;
@@ -224,10 +225,9 @@ static void thread_func(void *userdata) {
                     if (u->encoded_memchunk.length <= 0) {
                         /* Encode it */
                         size_t rl = u->raw_memchunk.length;
-                        if (u->encoded_memchunk.memblock)
-                            pa_memblock_unref(u->encoded_memchunk.memblock);
+                        u->encoding_overhead += u->next_encoding_overhead;
                         u->encoded_memchunk = pa_raop_client_encode_sample(u->raop, u->core->mempool, &u->raw_memchunk);
-                        u->encoding_overhead += (u->encoded_memchunk.length - (rl - u->raw_memchunk.length));
+                        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);
                     }
                     pa_assert(u->encoded_memchunk.length > 0);
@@ -259,11 +259,6 @@ static void thread_func(void *userdata) {
                         u->encoded_memchunk.index += l;
                         u->encoded_memchunk.length -= l;
 
-                        if (u->encoded_memchunk.length <= 0) {
-                            pa_memblock_unref(u->encoded_memchunk.memblock);
-                            pa_memchunk_reset(&u->encoded_memchunk);
-                        }
-
                         pollfd->revents = 0;
 
                         if (u->encoded_memchunk.length > 0)
@@ -381,6 +376,7 @@ int pa__init(pa_module*m) {
     pa_memchunk_reset(&u->encoded_memchunk);
     u->offset = 0;
     u->encoding_overhead = 0;
+    u->next_encoding_overhead = 0;
     u->encoding_ratio = 1.0;
 
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
@@ -477,9 +473,6 @@ void pa__done(pa_module*m) {
     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);
 
diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index bad747b..b4cbd2b 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -74,7 +74,7 @@
 
 struct pa_raop_client {
     pa_mainloop_api *mainloop;
-    const char *host;
+    char *host;
     char *sid;
     pa_rtsp_client *rtsp;
 
@@ -93,9 +93,10 @@ struct pa_raop_client {
     void* userdata;
 
     uint8_t *buffer;
+    uint32_t buffer_length;
     uint8_t *buffer_index;
     uint16_t buffer_count;
-    /*pa_memchunk memchunk;*/
+    pa_memchunk memchunk;
 };
 
 /**
@@ -356,6 +357,7 @@ pa_raop_client* pa_raop_client_new(pa_mainloop_api *mainloop, const char* host)
     c->mainloop = mainloop;
     c->fd = -1;
     c->host = pa_xstrdup(host);
+    pa_memchunk_reset(&c->memchunk);
     c->rtsp = pa_rtsp_client_new("iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
 
     /* Initialise the AES encryption system */
@@ -386,6 +388,9 @@ void pa_raop_client_free(pa_raop_client* c)
 {
     pa_assert(c);
 
+    if (c->memchunk.memblock)
+        pa_memblock_unref(c->memchunk.memblock);
+    pa_xfree(c->buffer);
     pa_rtsp_client_free(c->rtsp);
     pa_xfree(c->aes_iv);
     pa_xfree(c->aes_nv);
@@ -403,9 +408,8 @@ pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_mempool* mempool,
     uint8_t *bp, bpos;
     uint8_t *ibp, *maxibp;
     int size;
-    uint8_t *p;
+    uint8_t *b, *p;
     uint16_t bsize;
-    pa_memchunk rv;
     size_t length;
     static uint8_t header[] = {
         0x24, 0x00, 0x00, 0x00,
@@ -427,13 +431,22 @@ pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_mempool* mempool,
 
     /* Leave 16 bytes extra to allow for the ALAC header which is about 55 bits */
     bufmax = length + header_size + 16;
-    c->buffer = pa_xrealloc(c->buffer, bufmax);
-    memcpy(c->buffer, header, header_size);
-    pa_memchunk_reset(&rv);
-    rv.memblock = pa_memblock_new_user(mempool, c->buffer, (header_size + length), noop, 1);
+    if (bufmax > c->buffer_length) {
+        if (c->memchunk.memblock)
+            pa_memblock_unref(c->memchunk.memblock);
+
+        c->buffer = pa_xrealloc(c->buffer, bufmax);
+        c->buffer_length = bufmax;
+        pa_log_debug("Creating new memblock");
+        c->memchunk.memblock = pa_memblock_new_user(mempool, c->buffer, bufmax, noop, 0);
+    }
+    c->memchunk.index = 0;
+    c->memchunk.length = 0;
+    b = pa_memblock_acquire(c->memchunk.memblock);
+    memcpy(b, header, header_size);
 
     /* Now write the actual samples */
-    bp = c->buffer + header_size;
+    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
@@ -462,16 +475,20 @@ pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_mempool* mempool,
         raw->length -= 4;
     }
     pa_memblock_release(raw->memblock);
-    rv.length = header_size + size;
+    c->memchunk.length = header_size + size;
 
     /* store the lenght (endian swapped: make this better) */
     len = size + header_size - 4;
-    *(c->buffer + 2) = len >> 8;
-    *(c->buffer + 3) = len & 0xff;
+    *(b + 2) = len >> 8;
+    *(b + 3) = len & 0xff;
 
     /* encrypt our data */
-    aes_encrypt(c, (c->buffer + header_size), size);
-    return rv;
+    aes_encrypt(c, (b + header_size), size);
+
+    /* We're done with the chunk */
+    pa_memblock_release(c->memchunk.memblock);
+
+    return c->memchunk;
 }
 
 

commit 899492c31581f5591cd9437052dda15ad02ec0ac
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 14:18:48 2008 +0000

    Add a new callback structure to propigate when the RTSP connection dies
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2402 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index b4cbd2b..75881c6 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -89,8 +89,11 @@ struct pa_raop_client {
 
     pa_socket_client *sc;
     int fd;
+
     pa_raop_client_cb_t callback;
     void* userdata;
+    pa_raop_client_closed_cb_t closed_callback;
+    void* closed_userdata;
 
     uint8_t *buffer;
     uint32_t buffer_length;
@@ -339,6 +342,19 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* he
         case STATE_SET_PARAMETER:
         case STATE_FLUSH:
             break;
+        case STATE_DISCONNECTED:
+            pa_assert(c->closed_callback);
+            pa_log_debug("RTSP channel closed");
+            if (c->fd > 0) {
+                pa_close(c->fd);
+                c->fd = -1;
+            }
+            if (c->sc) {
+                pa_socket_client_unref(c->sc);
+                c->sc = NULL;
+            }
+            c->closed_callback(c->closed_userdata);
+            break;
     }
 }
 
@@ -437,7 +453,6 @@ pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_mempool* mempool,
 
         c->buffer = pa_xrealloc(c->buffer, bufmax);
         c->buffer_length = bufmax;
-        pa_log_debug("Creating new memblock");
         c->memchunk.memblock = pa_memblock_new_user(mempool, c->buffer, bufmax, noop, 0);
     }
     c->memchunk.index = 0;
@@ -499,3 +514,11 @@ void pa_raop_client_set_callback(pa_raop_client* c, pa_raop_client_cb_t callback
     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)
+{
+    pa_assert(c);
+
+    c->closed_callback = callback;
+    c->closed_userdata = userdata;
+}
diff --git a/src/modules/rtp/raop_client.h b/src/modules/rtp/raop_client.h
index 68a1cdb..1ec56ca 100644
--- a/src/modules/rtp/raop_client.h
+++ b/src/modules/rtp/raop_client.h
@@ -38,4 +38,7 @@ pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_mempool* mempool,
 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);
 
+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);
+
 #endif
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index 5665c9f..22f0f0c 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -173,6 +173,7 @@ static void headers_read(pa_rtsp_client *c) {
 
     pa_assert(c);
     pa_assert(c->response_headers);
+    pa_assert(c->callback);
 
     /* Deal with a SETUP response */
     if (STATE_SETUP == c->state) {
@@ -209,8 +210,7 @@ static void headers_read(pa_rtsp_client *c) {
     }
 
     /* Call our callback */
-    if (c->callback)
-        c->callback(c, c->state, c->response_headers, c->userdata);
+    c->callback(c, c->state, c->response_headers, c->userdata);
 
     pa_headerlist_free(c->response_headers);
     c->response_headers = NULL;
@@ -224,12 +224,13 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
     pa_rtsp_client *c = userdata;
     pa_assert(line);
     pa_assert(c);
+    pa_assert(c->callback);
 
     if (!s) {
-        pa_log_warn("Connection closed");
         pa_ioline_unref(c->ioline);
         c->ioline = NULL;
         pa_rtsp_disconnect(c);
+        c->callback(c, STATE_DISCONNECTED, NULL, c->userdata);
         return;
     }
 
diff --git a/src/modules/rtp/rtsp_client.h b/src/modules/rtp/rtsp_client.h
index 0f1daab..3c5280c 100644
--- a/src/modules/rtp/rtsp_client.h
+++ b/src/modules/rtp/rtsp_client.h
@@ -44,7 +44,8 @@ typedef enum {
   STATE_RECORD,
   STATE_TEARDOWN,
   STATE_SET_PARAMETER,
-  STATE_FLUSH
+  STATE_FLUSH,
+  STATE_DISCONNECTED
 } pa_rtsp_state;
 typedef void (*pa_rtsp_cb_t)(pa_rtsp_client *c, pa_rtsp_state state, pa_headerlist* hl, void *userdata);
 

commit ec9a618768790055fef00a46866b4e0e1fa33048
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 14:19:41 2008 +0000

    Listen to the on_close callback. This still causes asserts in the mainloop, so this is not a complete solution
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2403 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index f6f93a4..090f04f 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -340,6 +340,14 @@ static void on_connection(PA_GCC_UNUSED int fd, void*userdata) {
     pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL);
 }
 
+static void on_close(void*userdata) {
+    struct userdata *u = userdata;
+    pa_assert(u);
+
+    pa_log_debug("Control connection closed.");
+    pa_module_unload_request(u->module);
+}
+
 int pa__init(pa_module*m) {
     struct userdata *u = NULL;
     const char *p;
@@ -420,6 +428,7 @@ int pa__init(pa_module*m) {
     }
 
     pa_raop_client_set_callback(u->raop, on_connection, u);
+    pa_raop_client_set_closed_callback(u->raop, on_close, u);
     pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Airtunes sink '%s'", p));
     pa_xfree(t);
 

commit e00127fe245cc2065f74617dada3b474b88907af
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 14:57:30 2008 +0000

    Various changes suggested by Lennart.
    
    Store the core* rather than just the mainloop as we can reuse the mempool without passing it in as an argument.
    const'ify and deconst'ify some vars
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2404 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 090f04f..0a7ce17 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -226,7 +226,7 @@ static void thread_func(void *userdata) {
                         /* Encode it */
                         size_t rl = u->raw_memchunk.length;
                         u->encoding_overhead += u->next_encoding_overhead;
-                        u->encoded_memchunk = pa_raop_client_encode_sample(u->raop, u->core->mempool, &u->raw_memchunk);
+                        u->encoded_memchunk = pa_raop_client_encode_sample(u->raop, &u->raw_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);
                     }
@@ -422,7 +422,7 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    if (!(u->raop = pa_raop_client_new(u->core->mainloop, p))) {
+    if (!(u->raop = pa_raop_client_new(u->core, p))) {
         pa_log("Failed to connect to server.");
         goto fail;
     }
diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index 75881c6..92be6cd 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -73,7 +73,7 @@
 
 
 struct pa_raop_client {
-    pa_mainloop_api *mainloop;
+    pa_core *core;
     char *host;
     char *sid;
     pa_rtsp_client *rtsp;
@@ -155,14 +155,14 @@ 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) {
-    char n[] =
+    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==";
-    char e[] = "AQAB";
+    const char e[] = "AQAB";
     uint8_t modules[256];
     uint8_t exponent[8];
     int size;
@@ -330,7 +330,7 @@ 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->mainloop, c->host, port))) {
+            if (!(c->sc = pa_socket_client_new_string(c->core->mainloop, c->host, port))) {
                 pa_log("failed to connect to server '%s:%d'", c->host, port);
                 return;
             }
@@ -358,7 +358,7 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* he
     }
 }
 
-pa_raop_client* pa_raop_client_new(pa_mainloop_api *mainloop, const char* host)
+pa_raop_client* pa_raop_client_new(pa_core *core, const char* host)
 {
     char *sci;
     struct {
@@ -368,16 +368,16 @@ pa_raop_client* pa_raop_client_new(pa_mainloop_api *mainloop, const char* host)
     } rand_data;
     pa_raop_client* c = pa_xnew0(pa_raop_client, 1);
 
+    pa_assert(core);
     pa_assert(host);
 
-    c->mainloop = mainloop;
+    c->core = core;
     c->fd = -1;
     c->host = pa_xstrdup(host);
     pa_memchunk_reset(&c->memchunk);
     c->rtsp = pa_rtsp_client_new("iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
 
     /* Initialise the AES encryption system */
-    pa_random_seed();
     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));
@@ -389,7 +389,7 @@ pa_raop_client* pa_raop_client_new(pa_mainloop_api *mainloop, const char* host)
     sci = pa_sprintf_malloc("%08x%08x",rand_data.b, rand_data.c);
     pa_rtsp_add_header(c->rtsp, "Client-Instance", sci);
     pa_rtsp_set_callback(c->rtsp, rtsp_cb, c);
-    if (pa_rtsp_connect(c->rtsp, mainloop, host, 5000)) {
+    if (pa_rtsp_connect(c->rtsp, c->core->mainloop, host, 5000)) {
         pa_rtsp_client_free(c->rtsp);
         pa_xfree(c->aes_iv);
         pa_xfree(c->aes_nv);
@@ -418,14 +418,14 @@ void pa_raop_client_free(pa_raop_client* c)
 
 static void noop(PA_GCC_UNUSED void* p) {}
 
-pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_mempool* mempool, pa_memchunk* raw)
+pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw)
 {
     uint16_t len, bufmax;
     uint8_t *bp, bpos;
     uint8_t *ibp, *maxibp;
     int size;
     uint8_t *b, *p;
-    uint16_t bsize;
+    uint32_t bsize;
     size_t length;
     static uint8_t header[] = {
         0x24, 0x00, 0x00, 0x00,
@@ -433,7 +433,7 @@ pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_mempool* mempool,
         0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00,
     };
-    const int header_size = sizeof(header);
+    int header_size = sizeof(header);
 
     pa_assert(c);
     pa_assert(c->fd > 0);
@@ -453,7 +453,7 @@ pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_mempool* mempool,
 
         c->buffer = pa_xrealloc(c->buffer, bufmax);
         c->buffer_length = bufmax;
-        c->memchunk.memblock = pa_memblock_new_user(mempool, c->buffer, bufmax, noop, 0);
+        c->memchunk.memblock = pa_memblock_new_user(c->core->mempool, c->buffer, bufmax, noop, 0);
     }
     c->memchunk.index = 0;
     c->memchunk.length = 0;
diff --git a/src/modules/rtp/raop_client.h b/src/modules/rtp/raop_client.h
index 1ec56ca..b2817e5 100644
--- a/src/modules/rtp/raop_client.h
+++ b/src/modules/rtp/raop_client.h
@@ -26,14 +26,14 @@
 
 #include <pulse/mainloop-api.h>
 #include <pulsecore/iochannel.h>
-#include <pulsecore/memchunk.h>
+#include <pulsecore/core.h>
 
 typedef struct pa_raop_client pa_raop_client;
 
-pa_raop_client* pa_raop_client_new(pa_mainloop_api *mainloop, const char* host);
+pa_raop_client* pa_raop_client_new(pa_core *core, const char* host);
 void pa_raop_client_free(pa_raop_client* c);
 
-pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_mempool* mempool, pa_memchunk* raw);
+pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw);
 
 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);

commit d195d06da7009db985c0a5827b096bc39dd994bf
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 15:06:14 2008 +0000

    Change suggested by Lennart. Do not return a memchunk, instead pass in the pointer.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2405 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 0a7ce17..3d0eaef 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -226,7 +226,7 @@ static void thread_func(void *userdata) {
                         /* Encode it */
                         size_t rl = u->raw_memchunk.length;
                         u->encoding_overhead += u->next_encoding_overhead;
-                        u->encoded_memchunk = pa_raop_client_encode_sample(u->raop, &u->raw_memchunk);
+                        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);
                     }
@@ -482,6 +482,9 @@ void pa__done(pa_module*m) {
     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);
 
diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index 92be6cd..1581017 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -96,10 +96,9 @@ struct pa_raop_client {
     void* closed_userdata;
 
     uint8_t *buffer;
-    uint32_t buffer_length;
+    size_t buffer_length;
     uint8_t *buffer_index;
     uint16_t buffer_count;
-    pa_memchunk memchunk;
 };
 
 /**
@@ -374,7 +373,6 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char* host)
     c->core = core;
     c->fd = -1;
     c->host = pa_xstrdup(host);
-    pa_memchunk_reset(&c->memchunk);
     c->rtsp = pa_rtsp_client_new("iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
 
     /* Initialise the AES encryption system */
@@ -404,8 +402,6 @@ void pa_raop_client_free(pa_raop_client* c)
 {
     pa_assert(c);
 
-    if (c->memchunk.memblock)
-        pa_memblock_unref(c->memchunk.memblock);
     pa_xfree(c->buffer);
     pa_rtsp_client_free(c->rtsp);
     pa_xfree(c->aes_iv);
@@ -418,9 +414,10 @@ void pa_raop_client_free(pa_raop_client* c)
 
 static void noop(PA_GCC_UNUSED void* p) {}
 
-pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw)
+int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchunk* encoded)
 {
-    uint16_t len, bufmax;
+    uint16_t len;
+    size_t bufmax;
     uint8_t *bp, bpos;
     uint8_t *ibp, *maxibp;
     int size;
@@ -440,6 +437,7 @@ pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw)
     pa_assert(raw);
     pa_assert(raw->memblock);
     pa_assert(raw->length > 0);
+    pa_assert(encoded);
 
     /* We have to send 4 byte chunks */
     bsize = (int)(raw->length / 4);
@@ -448,16 +446,16 @@ pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw)
     /* Leave 16 bytes extra to allow for the ALAC header which is about 55 bits */
     bufmax = length + header_size + 16;
     if (bufmax > c->buffer_length) {
-        if (c->memchunk.memblock)
-            pa_memblock_unref(c->memchunk.memblock);
+        if (encoded->memblock)
+            pa_memblock_unref(encoded->memblock);
 
         c->buffer = pa_xrealloc(c->buffer, bufmax);
         c->buffer_length = bufmax;
-        c->memchunk.memblock = pa_memblock_new_user(c->core->mempool, c->buffer, bufmax, noop, 0);
+        encoded->memblock = pa_memblock_new_user(c->core->mempool, c->buffer, bufmax, noop, 0);
     }
-    c->memchunk.index = 0;
-    c->memchunk.length = 0;
-    b = pa_memblock_acquire(c->memchunk.memblock);
+    encoded->index = 0;
+    encoded->length = 0;
+    b = pa_memblock_acquire(encoded->memblock);
     memcpy(b, header, header_size);
 
     /* Now write the actual samples */
@@ -490,7 +488,7 @@ pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw)
         raw->length -= 4;
     }
     pa_memblock_release(raw->memblock);
-    c->memchunk.length = header_size + size;
+    encoded->length = header_size + size;
 
     /* store the lenght (endian swapped: make this better) */
     len = size + header_size - 4;
@@ -501,9 +499,9 @@ pa_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw)
     aes_encrypt(c, (b + header_size), size);
 
     /* We're done with the chunk */
-    pa_memblock_release(c->memchunk.memblock);
+    pa_memblock_release(encoded->memblock);
 
-    return c->memchunk;
+    return 0;
 }
 
 
diff --git a/src/modules/rtp/raop_client.h b/src/modules/rtp/raop_client.h
index b2817e5..303fdaa 100644
--- a/src/modules/rtp/raop_client.h
+++ b/src/modules/rtp/raop_client.h
@@ -33,7 +33,7 @@ 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_memchunk pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw);
+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);

commit 4b7b7b15d73a5f2a98229b12406b4397563d2983
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 15:12:20 2008 +0000

    Fix up IPv6 address format to enclose it in []
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2406 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index 22f0f0c..193248e 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -352,12 +352,14 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
         const char *res = NULL;
 
         if (AF_INET == sa.sa.sa_family) {
-            res = inet_ntop(sa.sa.sa_family, &sa.in.sin_addr, buf, sizeof(buf));
+            if ((res = inet_ntop(sa.sa.sa_family, &sa.in.sin_addr, buf, sizeof(buf)))) {
+                c->localip = pa_xstrdup(res);
+            }
         } else if (AF_INET6 == sa.sa.sa_family) {
-            res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf));
+            if ((res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf)))) {
+                c->localip = pa_sprintf_malloc("[%s]", res);
+            }
         }
-        if (res)
-            c->localip = pa_xstrdup(res);
     }
     pa_log_debug("Established RTSP connection from local ip %s", c->localip);
 

commit cb8c5a925fc22819626cbe4525b1d334db75d071
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 15:34:37 2008 +0000

    Some misc fixes. consts, base64 optimisation (not that it will be with us long anyway), and c comments
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2407 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/base64.c b/src/modules/rtp/base64.c
index 043ef5a..980b018 100644
--- a/src/modules/rtp/base64.c
+++ b/src/modules/rtp/base64.c
@@ -37,16 +37,16 @@
 
 #include "base64.h"
 
-static char base64_chars[] =
+static const char base64_chars[] =
     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 static int pos(char c)
 {
-    char *p;
-    for (p = base64_chars; *p; p++)
-        if (*p == c)
-            return p - base64_chars;
-    return -1;
+    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;
 }
 
 int pa_base64_encode(const void *data, int size, char **str)
diff --git a/src/modules/rtp/headerlist.c b/src/modules/rtp/headerlist.c
index 8bdc725..de8710b 100644
--- a/src/modules/rtp/headerlist.c
+++ b/src/modules/rtp/headerlist.c
@@ -102,7 +102,7 @@ int pa_headerlist_putsappend(pa_headerlist *p, const char *key, const char *valu
         hdr->value = pa_xstrdup(value);
         add = TRUE;
     } else {
-        void *newval = (void*)pa_sprintf_malloc("%s%s", (char*)hdr->value, value);
+        void *newval = pa_sprintf_malloc("%s%s", (char*)hdr->value, value);
         pa_xfree(hdr->value);
         hdr->value = newval;
     }
diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index 1581017..fc42340 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -461,13 +461,13 @@ int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchun
     /* 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
+    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);

commit be73d378f5fd5ea1aedcc75b0c7c1e1a82a27047
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 15:43:56 2008 +0000

    unref the raw data memblock before requesting more data.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2408 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 3d0eaef..79c517a 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -217,6 +217,10 @@ static void thread_func(void *userdata) {
                     void *p;
 
                     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);
                     }

commit eca94fee59ba68603f1c34f5a4abe55dd7d9d638
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 16:38:33 2008 +0000

    Don't try to free stack variables.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2409 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index fc42340..a9b9ab1 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -389,9 +389,6 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char* host)
     pa_rtsp_set_callback(c->rtsp, rtsp_cb, c);
     if (pa_rtsp_connect(c->rtsp, c->core->mainloop, host, 5000)) {
         pa_rtsp_client_free(c->rtsp);
-        pa_xfree(c->aes_iv);
-        pa_xfree(c->aes_nv);
-        pa_xfree(c->aes_key);
         return NULL;
     }
     return c;
@@ -404,9 +401,6 @@ void pa_raop_client_free(pa_raop_client* c)
 
     pa_xfree(c->buffer);
     pa_rtsp_client_free(c->rtsp);
-    pa_xfree(c->aes_iv);
-    pa_xfree(c->aes_nv);
-    pa_xfree(c->aes_key);
     pa_xfree(c->host);
     pa_xfree(c);
 }

commit 92166846913ebb5e86f36352e20c9ca4f4bf23ae
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 16:40:26 2008 +0000

    Do not prefix internal function rtsp_exec.
    
    Change port to be 16 bits
    Do not free stuff on closure as this happens further up the stack.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2410 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index 193248e..2483972 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -70,13 +70,13 @@ struct pa_rtsp_client {
 
     char *localip;
     char *url;
-    uint32_t port;
+    uint16_t port;
     uint32_t cseq;
     char *session;
     char *transport;
 };
 
-static int pa_rtsp_exec(pa_rtsp_client* c, const char* cmd,
+static int rtsp_exec(pa_rtsp_client* c, const char* cmd,
                         const char* content_type, const char* content,
                         int expect_response,
                         pa_headerlist* headers) {
@@ -193,7 +193,7 @@ static void headers_read(pa_rtsp_client *c) {
         while ((token = pa_split(c->transport, delimiters, &token_state))) {
             if ((pc = strstr(token, "="))) {
                 if (0 == strncmp(token, "server_port", 11)) {
-                    pa_atou(pc+1, &c->port);
+                    pa_atou(pc+1, (uint32_t*)(&c->port));
                     pa_xfree(token);
                     break;
                 }
@@ -227,9 +227,6 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
     pa_assert(c->callback);
 
     if (!s) {
-        pa_ioline_unref(c->ioline);
-        c->ioline = NULL;
-        pa_rtsp_disconnect(c);
         c->callback(c, STATE_DISCONNECTED, NULL, c->userdata);
         return;
     }
@@ -442,7 +439,7 @@ int pa_rtsp_announce(pa_rtsp_client *c, const char* sdp) {
         return -1;
 
     c->state = STATE_ANNOUNCE;
-    return pa_rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL);
+    return rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL);
 }
 
 
@@ -456,7 +453,7 @@ int pa_rtsp_setup(pa_rtsp_client* c) {
     pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
 
     c->state = STATE_SETUP;
-    rv = pa_rtsp_exec(c, "SETUP", NULL, NULL, 1, headers);
+    rv = rtsp_exec(c, "SETUP", NULL, NULL, 1, headers);
     pa_headerlist_free(headers);
     return rv;
 }
@@ -477,7 +474,7 @@ int pa_rtsp_record(pa_rtsp_client* c) {
     pa_headerlist_puts(headers, "RTP-Info", "seq=0;rtptime=0");
 
     c->state = STATE_RECORD;
-    rv = pa_rtsp_exec(c, "RECORD", NULL, NULL, 1, headers);
+    rv = rtsp_exec(c, "RECORD", NULL, NULL, 1, headers);
     pa_headerlist_free(headers);
     return rv;
 }
@@ -487,7 +484,7 @@ int pa_rtsp_teardown(pa_rtsp_client *c) {
     pa_assert(c);
 
     c->state = STATE_TEARDOWN;
-    return pa_rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
+    return rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
 }
 
 
@@ -497,7 +494,7 @@ int pa_rtsp_setparameter(pa_rtsp_client *c, const char* param) {
         return -1;
 
     c->state = STATE_SET_PARAMETER;
-    return pa_rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL);
+    return rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL);
 }
 
 
@@ -511,7 +508,7 @@ int pa_rtsp_flush(pa_rtsp_client *c) {
     pa_headerlist_puts(headers, "RTP-Info", "seq=0;rtptime=0");
 
     c->state = STATE_FLUSH;
-    rv = pa_rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers);
+    rv = rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers);
     pa_headerlist_free(headers);
     return rv;
 }

commit 3767cdb6d16c5817eb489129585fb353e3ad6afa
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun May 11 17:02:19 2008 +0000

    Do tidy up on disconnection.
    
    Only clear IO related stuff if this free() was triggered deliberatly (i.e. not by server side disconnect)
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2411 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index a9b9ab1..e5a373d 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -344,6 +344,7 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* he
         case STATE_DISCONNECTED:
             pa_assert(c->closed_callback);
             pa_log_debug("RTSP channel closed");
+            c->rtsp = NULL;
             if (c->fd > 0) {
                 pa_close(c->fd);
                 c->fd = -1;
@@ -400,7 +401,8 @@ void pa_raop_client_free(pa_raop_client* c)
     pa_assert(c);
 
     pa_xfree(c->buffer);
-    pa_rtsp_client_free(c->rtsp);
+    if (c->rtsp)
+        pa_rtsp_client_free(c->rtsp);
     pa_xfree(c->host);
     pa_xfree(c);
 }
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index 2483972..f9fe9bf 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -151,6 +151,10 @@ void pa_rtsp_client_free(pa_rtsp_client* c) {
     if (c) {
         if (c->sc)
             pa_socket_client_unref(c->sc);
+        if (c->ioline)
+            pa_ioline_close(c->ioline);
+        else if (c->io)
+            pa_iochannel_free(c->io);
 
         pa_xfree(c->url);
         pa_xfree(c->localip);
@@ -227,6 +231,10 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
     pa_assert(c->callback);
 
     if (!s) {
+        /* Keep the ioline/iochannel open as they will be freed automatically */
+        c->ioline = NULL;
+        c->io = NULL;
+        pa_rtsp_client_free(c);
         c->callback(c, STATE_DISCONNECTED, NULL, c->userdata);
         return;
     }

commit 6c1dd6e54b4b5b4213467d156abc9f260c63aaa3
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Mon May 26 21:04:45 2008 +0000

    Move the encoding loop around a bit such that it does not grab the data and keep it for the next loop iteration.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2481 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 79c517a..e17198c 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -216,17 +216,19 @@ static void thread_func(void *userdata) {
                     ssize_t l;
                     void *p;
 
-                    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);
-
                     if (u->encoded_memchunk.length <= 0) {
+                        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);
+                            p = pa_memblock_acquire(u->raw_memchunk.memblock);
+                            pa_memblock_release(u->raw_memchunk.memblock);
+                        }
+                        pa_assert(u->raw_memchunk.length > 0);
+
                         /* Encode it */
                         size_t rl = u->raw_memchunk.length;
                         u->encoding_overhead += u->next_encoding_overhead;

commit 6dc5e0797738d6afe2c3c99750abd2fb26fed9a9
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Mon May 26 21:05:53 2008 +0000

    Set the send buffer size to prevent rendering silence in amongst our good data (this should be more sophisticated but that can wait for a glitch-free port)
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2482 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index e5a373d..cdf9150 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -227,6 +227,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
     c->fd = pa_iochannel_get_send_fd(io);
 
     pa_iochannel_set_noclose(io, TRUE);
+    pa_iochannel_socket_set_sndbuf(io, 1024);
     pa_iochannel_free(io);
 
     pa_make_tcp_socket_low_delay(c->fd);

commit 8108121fa76e51c36772a592f74b075dcaf7c4cb
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Mon May 26 21:10:08 2008 +0000

    Set forgotten keyword property
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2483 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index e17198c..42092e0 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -1,4 +1,4 @@
-/* $Id: module-esound-sink.c 2043 2007-11-09 18:25:40Z lennart $ */
+/* $Id$ */
 
 /***
   This file is part of PulseAudio.

commit b93e9e80ecbdc90c0bf6f90c5bd4aafcdb4d2488
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Mon May 26 23:02:30 2008 +0000

    Keep track of the memblock pointer internally and do not rely on subsequent calls to pass it back in for unref'ing
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2484 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index cdf9150..7ba1be7 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -96,6 +96,7 @@ struct pa_raop_client {
     void* closed_userdata;
 
     uint8_t *buffer;
+    pa_memblock *memblock;
     size_t buffer_length;
     uint8_t *buffer_index;
     uint16_t buffer_count;
@@ -443,15 +444,14 @@ int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchun
     /* Leave 16 bytes extra to allow for the ALAC header which is about 55 bits */
     bufmax = length + header_size + 16;
     if (bufmax > c->buffer_length) {
-        if (encoded->memblock)
-            pa_memblock_unref(encoded->memblock);
-
         c->buffer = pa_xrealloc(c->buffer, bufmax);
         c->buffer_length = bufmax;
-        encoded->memblock = pa_memblock_new_user(c->core->mempool, c->buffer, bufmax, noop, 0);
+        if (c->memblock)
+            pa_memblock_unref(c->memblock);
+        c->memblock = pa_memblock_new_user(c->core->mempool, c->buffer, bufmax, noop, 0);
     }
-    encoded->index = 0;
-    encoded->length = 0;
+    pa_memchunk_reset(encoded);
+    encoded->memblock = c->memblock;
     b = pa_memblock_acquire(encoded->memblock);
     memcpy(b, header, header_size);
 

commit 13bc07587564977a64222f5a4075b7fa63ac5548
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Mon May 26 23:43:51 2008 +0000

    A few related changes:
    
    * Change the encode_sample routine to simply return normal memchunks allocated from the mempool.
    * unref the memchunks returned from encode_sample when we are done with them.
    * Create an encoded 'silence' sample and play this at all times to prevent hangup and to 'hog' the airtunes device
    
    This now works and can be used as a regular sink albeit with a constant latency of about 8 seconds :s
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2485 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 42092e0..f78abba 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -178,7 +178,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             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->events = pollfd->revents = 0;
+            pollfd->events = POLLOUT;
+            pollfd->revents = 0;
 
             return 0;
         }
@@ -190,6 +191,9 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 static void thread_func(void *userdata) {
     struct userdata *u = userdata;
     int write_type = 0;
+    pa_memchunk silence;
+    uint32_t silence_overhead;
+    double silence_ratio;
 
     pa_assert(u);
 
@@ -200,6 +204,9 @@ static void thread_func(void *userdata) {
 
     pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
 
+    /* Create a chunk of memory that is our encoded silence sample. */
+    pa_memchunk_reset(&silence);
+
     for (;;) {
         int ret;
 
@@ -208,33 +215,61 @@ static void thread_func(void *userdata) {
             pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
 
             /* Render some data and write it to the fifo */
-            if (PA_SINK_OPENED(u->sink->thread_info.state) && pollfd->revents) {
+            if (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;
-                    void *p;
 
                     if (u->encoded_memchunk.length <= 0) {
-                        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);
-                            p = pa_memblock_acquire(u->raw_memchunk.memblock);
-                            pa_memblock_release(u->raw_memchunk.memblock);
+                        if (u->encoded_memchunk.memblock)
+                            pa_memblock_unref(u->encoded_memchunk.memblock);
+                        if (PA_SINK_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 */
+                            u->encoding_overhead += u->next_encoding_overhead;
+                            memcpy(&u->encoded_memchunk, &silence, sizeof(pa_memchunk));
+                            pa_memblock_ref(silence.memblock);
+                            u->next_encoding_overhead = silence_overhead;
+                            u->encoding_ratio = silence_ratio;
                         }
-                        pa_assert(u->raw_memchunk.length > 0);
-
-                        /* Encode it */
-                        size_t 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);
                     }
                     pa_assert(u->encoded_memchunk.length > 0);
 
@@ -303,7 +338,7 @@ static void thread_func(void *userdata) {
             }
 
             /* Hmm, nothing to do. Let's sleep */
-            pollfd->events = PA_SINK_OPENED(u->sink->thread_info.state)  ? POLLOUT : 0;
+            /* pollfd->events = PA_SINK_OPENED(u->sink->thread_info.state)  ? POLLOUT : 0; */
         }
 
         if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
@@ -331,6 +366,8 @@ fail:
     pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
 
 finish:
+    if (silence.memblock)
+        pa_memblock_unref(silence.memblock);
     pa_log_debug("Thread shutting down");
 }
 
diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index 7ba1be7..4085a49 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -94,12 +94,6 @@ struct pa_raop_client {
     void* userdata;
     pa_raop_client_closed_cb_t closed_callback;
     void* closed_userdata;
-
-    uint8_t *buffer;
-    pa_memblock *memblock;
-    size_t buffer_length;
-    uint8_t *buffer_index;
-    uint16_t buffer_count;
 };
 
 /**
@@ -402,7 +396,6 @@ void pa_raop_client_free(pa_raop_client* c)
 {
     pa_assert(c);
 
-    pa_xfree(c->buffer);
     if (c->rtsp)
         pa_rtsp_client_free(c->rtsp);
     pa_xfree(c->host);
@@ -443,15 +436,8 @@ int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchun
 
     /* Leave 16 bytes extra to allow for the ALAC header which is about 55 bits */
     bufmax = length + header_size + 16;
-    if (bufmax > c->buffer_length) {
-        c->buffer = pa_xrealloc(c->buffer, bufmax);
-        c->buffer_length = bufmax;
-        if (c->memblock)
-            pa_memblock_unref(c->memblock);
-        c->memblock = pa_memblock_new_user(c->core->mempool, c->buffer, bufmax, noop, 0);
-    }
     pa_memchunk_reset(encoded);
-    encoded->memblock = c->memblock;
+    encoded->memblock = pa_memblock_new(c->core->mempool, bufmax);
     b = pa_memblock_acquire(encoded->memblock);
     memcpy(b, header, header_size);
 

commit 7f0cf0c9adc5176ba41d549754ea25ed6f12bdce
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Tue Jun 3 23:07:48 2008 +0000

    Fix up a couple of values related to encoding overhead.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2497 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index f78abba..96c98a6 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -317,13 +317,13 @@ static void thread_func(void *userdata) {
                  * fully filled up. This is the best time to estimate
                  * the playback position of the server */
 
-                n = u->offset;
+                n = u->offset - u->encoding_overhead;
 
 #ifdef SIOCOUTQ
                 {
                     int l;
                     if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
-                        n -= l;
+                        n -= (l / u->encoding_ratio);
                 }
 #endif
 

commit 651da7d095f78e930fb758442905d5769cfda1c5
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Mon Jun 9 21:59:00 2008 +0000

    Minor update to copywrite (I still plan to replace this completely but in the mean time....)
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2499 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/base64.h b/src/modules/rtp/base64.h
index 199c635..347a997 100644
--- a/src/modules/rtp/base64.h
+++ b/src/modules/rtp/base64.h
@@ -7,6 +7,7 @@
   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
@@ -26,7 +27,7 @@
 
 /*
   This file was originally inspired by a file developed by
-    Kungliga Tekniska Högskolan
+    Kungliga Tekniska Høgskolan
 */
 
 int pa_base64_encode(const void *data, int size, char **str);

commit 5f527dc47944bbd97a49e8d89427d09850b28e5d
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Mon Jun 9 21:59:41 2008 +0000

    Add seq and rtptime params to record/flush with a view to using these for timing and device suspension
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2500 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index 4085a49..4714d27 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -90,6 +90,9 @@ struct pa_raop_client {
     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;
@@ -317,7 +320,7 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* he
             } else {
                 pa_log_warn("Audio Jack Status missing");
             }
-            pa_rtsp_record(c->rtsp);
+            pa_rtsp_record(c->rtsp, &c->seq, &c->rtptime);
             break;
         }
 
@@ -403,8 +406,6 @@ void pa_raop_client_free(pa_raop_client* c)
 }
 
 
-static void noop(PA_GCC_UNUSED void* p) {}
-
 int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchunk* encoded)
 {
     uint16_t len;
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index f9fe9bf..7004042 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -47,6 +47,8 @@
 #include <pulsecore/strbuf.h>
 #include <pulsecore/poll.h>
 #include <pulsecore/ioline.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/rtclock.h>
 
 #include "rtsp_client.h"
 
@@ -467,9 +469,10 @@ int pa_rtsp_setup(pa_rtsp_client* c) {
 }
 
 
-int pa_rtsp_record(pa_rtsp_client* c) {
+int pa_rtsp_record(pa_rtsp_client* c, uint16_t* seq, uint32_t* rtptime) {
     pa_headerlist* headers;
     int rv;
+    char *info;
 
     pa_assert(c);
     if (!c->session) {
@@ -477,9 +480,14 @@ int pa_rtsp_record(pa_rtsp_client* c) {
         return -1;
     }
 
+    /* Todo: Generate these values randomly as per spec */
+    *seq = *rtptime = 0;
+
     headers = pa_headerlist_new();
     pa_headerlist_puts(headers, "Range", "npt=0-");
-    pa_headerlist_puts(headers, "RTP-Info", "seq=0;rtptime=0");
+    info = pa_sprintf_malloc("seq=%u;rtptime=%u", *seq, *rtptime);
+    pa_headerlist_puts(headers, "RTP-Info", info);
+    pa_xfree(info);
 
     c->state = STATE_RECORD;
     rv = rtsp_exec(c, "RECORD", NULL, NULL, 1, headers);
@@ -506,14 +514,17 @@ int pa_rtsp_setparameter(pa_rtsp_client *c, const char* param) {
 }
 
 
-int pa_rtsp_flush(pa_rtsp_client *c) {
+int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime) {
     pa_headerlist* headers;
     int rv;
+    char *info;
 
     pa_assert(c);
 
     headers = pa_headerlist_new();
-    pa_headerlist_puts(headers, "RTP-Info", "seq=0;rtptime=0");
+    info = pa_sprintf_malloc("seq=%u;rtptime=%u", seq, rtptime);
+    pa_headerlist_puts(headers, "RTP-Info", info);
+    pa_xfree(info);
 
     c->state = STATE_FLUSH;
     rv = rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers);
diff --git a/src/modules/rtp/rtsp_client.h b/src/modules/rtp/rtsp_client.h
index 3c5280c..5554018 100644
--- a/src/modules/rtp/rtsp_client.h
+++ b/src/modules/rtp/rtsp_client.h
@@ -66,10 +66,10 @@ 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_setup(pa_rtsp_client* c);
-int pa_rtsp_record(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);
+int pa_rtsp_flush(pa_rtsp_client* c, uint16_t seq, uint32_t rtptime);
 
 #endif

commit 19dcb529ad3baa8e8e506ddfa703e952044e0f60
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Mon Jun 9 22:01:23 2008 +0000

    Remove unneeded headers accidentially added in r2500.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2501 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index 7004042..c0ca49f 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -47,8 +47,6 @@
 #include <pulsecore/strbuf.h>
 #include <pulsecore/poll.h>
 #include <pulsecore/ioline.h>
-#include <pulsecore/time-smoother.h>
-#include <pulsecore/rtclock.h>
 
 #include "rtsp_client.h"
 

commit d86fc75e0cbc9d102dc000d2781f9dfddc89fbbf
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Tue Jun 10 23:49:35 2008 +0000

    Change the API of the RTSP client a bit.
    
    * Store the mainloop, hostname and port internally on construction
    * This should allow use to easily reconnect if disconnected although this has thus far proved unreliable.
    The changes look like more than they are due to moving a function around.
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2502 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index c0ca49f..88f7781 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -51,6 +51,10 @@
 #include "rtsp_client.h"
 
 struct pa_rtsp_client {
+    pa_mainloop_api *mainloop;
+    char *hostname;
+    uint16_t port;
+
     pa_socket_client *sc;
     pa_iochannel *io;
     pa_ioline *ioline;
@@ -70,72 +74,23 @@ struct pa_rtsp_client {
 
     char *localip;
     char *url;
-    uint16_t port;
+    uint16_t rtp_port;
     uint32_t cseq;
     char *session;
     char *transport;
 };
 
-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;
-    ssize_t l;
-
-    pa_assert(c);
-    pa_assert(c->url);
-
-    if (!cmd)
-        return -1;
-
-    buf = pa_strbuf_new();
-    pa_strbuf_printf(buf, "%s %s RTSP/1.0\r\nCSeq: %d\r\n", cmd, c->url, ++c->cseq);
-    if (c->session)
-        pa_strbuf_printf(buf, "Session: %s\r\n", c->session);
-
-    /* Add the headers */
-    if (headers) {
-        hdrs = pa_headerlist_to_string(headers);
-        pa_strbuf_puts(buf, hdrs);
-        pa_xfree(hdrs);
-    }
-
-    if (content_type && content) {
-        pa_strbuf_printf(buf, "Content-Type: %s\r\nContent-Length: %d\r\n",
-          content_type, (int)strlen(content));
-    }
-
-    pa_strbuf_printf(buf, "User-Agent: %s\r\n", c->useragent);
-
-    if (c->headers) {
-        hdrs = pa_headerlist_to_string(c->headers);
-        pa_strbuf_puts(buf, hdrs);
-        pa_xfree(hdrs);
-    }
-
-    pa_strbuf_puts(buf, "\r\n");
-
-    if (content_type && content) {
-        pa_strbuf_puts(buf, content);
-    }
-
-    /* Our packet is created... now we can send it :) */
-    hdrs = pa_strbuf_tostring_free(buf);
-    /*pa_log_debug("Submitting request:");
-    pa_log_debug(hdrs);*/
-    l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));
-    pa_xfree(hdrs);
-
-    return 0;
-}
-
-
-pa_rtsp_client* pa_rtsp_client_new(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);
+    pa_assert(hostname);
+    pa_assert(port > 0);
+
     c = pa_xnew0(pa_rtsp_client, 1);
+    c->mainloop = mainloop;
+    c->hostname = pa_xstrdup(hostname);
+    c->port = port;
     c->headers = pa_headerlist_new();
 
     if (useragent)
@@ -156,6 +111,7 @@ void pa_rtsp_client_free(pa_rtsp_client* c) {
         else if (c->io)
             pa_iochannel_free(c->io);
 
+        pa_xfree(c->hostname);
         pa_xfree(c->url);
         pa_xfree(c->localip);
         pa_xfree(c->session);
@@ -197,14 +153,14 @@ static void headers_read(pa_rtsp_client *c) {
         while ((token = pa_split(c->transport, delimiters, &token_state))) {
             if ((pc = strstr(token, "="))) {
                 if (0 == strncmp(token, "server_port", 11)) {
-                    pa_atou(pc+1, (uint32_t*)(&c->port));
+                    pa_atou(pc+1, (uint32_t*)(&c->rtp_port));
                     pa_xfree(token);
                     break;
                 }
             }
             pa_xfree(token);
         }
-        if (0 == c->port) {
+        if (0 == c->rtp_port) {
             /* Error no server_port in response */
             pa_headerlist_free(c->response_headers);
             c->response_headers = NULL;
@@ -234,7 +190,6 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
         /* Keep the ioline/iochannel open as they will be freed automatically */
         c->ioline = NULL;
         c->io = NULL;
-        pa_rtsp_client_free(c);
         c->callback(c, STATE_DISCONNECTED, NULL, c->userdata);
         return;
     }
@@ -336,8 +291,8 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
 
     pa_assert(sc);
     pa_assert(c);
+    pa_assert(STATE_CONNECT == c->state);
     pa_assert(c->sc == sc);
-
     pa_socket_client_unref(c->sc);
     c->sc = NULL;
 
@@ -368,24 +323,24 @@ 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);
 
-    c->waiting = 1;
-    c->state = STATE_CONNECT;
     if (c->callback)
         c->callback(c, c->state, NULL, c->userdata);
 }
 
-int pa_rtsp_connect(pa_rtsp_client *c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port) {
+int pa_rtsp_connect(pa_rtsp_client *c) {
     pa_assert(c);
-    pa_assert(mainloop);
-    pa_assert(hostname);
-    pa_assert(port > 0);
+    pa_assert(!c->sc);
+
+    pa_xfree(c->session);
+    c->session = NULL;
 
-    if (!(c->sc = pa_socket_client_new_string(mainloop, hostname, port))) {
-        pa_log("failed to connect to server '%s:%d'", hostname, port);
+    if (!(c->sc = pa_socket_client_new_string(c->mainloop, c->hostname, c->port))) {
+        pa_log("failed to connect to server '%s:%d'", c->hostname, c->port);
         return -1;
     }
 
     pa_socket_client_set_callback(c->sc, on_connection, c);
+    c->waiting = 1;
     c->state = STATE_CONNECT;
     return 0;
 }
@@ -415,7 +370,7 @@ const char* pa_rtsp_localip(pa_rtsp_client* c) {
 uint32_t pa_rtsp_serverport(pa_rtsp_client* c) {
     pa_assert(c);
 
-    return c->port;
+    return c->rtp_port;
 }
 
 void pa_rtsp_set_url(pa_rtsp_client* c, const char* url) {
@@ -441,6 +396,64 @@ void pa_rtsp_remove_header(pa_rtsp_client *c, const char* 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,
+                        int expect_response,
+                        pa_headerlist* headers) {
+    pa_strbuf* buf;
+    char* hdrs;
+    ssize_t l;
+
+    pa_assert(c);
+    pa_assert(c->url);
+
+    if (!cmd)
+        return -1;
+
+    pa_log_debug("Sending command: %s", cmd);
+
+    buf = pa_strbuf_new();
+    pa_strbuf_printf(buf, "%s %s RTSP/1.0\r\nCSeq: %d\r\n", cmd, c->url, ++c->cseq);
+    if (c->session)
+        pa_strbuf_printf(buf, "Session: %s\r\n", c->session);
+
+    /* Add the headers */
+    if (headers) {
+        hdrs = pa_headerlist_to_string(headers);
+        pa_strbuf_puts(buf, hdrs);
+        pa_xfree(hdrs);
+    }
+
+    if (content_type && content) {
+        pa_strbuf_printf(buf, "Content-Type: %s\r\nContent-Length: %d\r\n",
+          content_type, (int)strlen(content));
+    }
+
+    pa_strbuf_printf(buf, "User-Agent: %s\r\n", c->useragent);
+
+    if (c->headers) {
+        hdrs = pa_headerlist_to_string(c->headers);
+        pa_strbuf_puts(buf, hdrs);
+        pa_xfree(hdrs);
+    }
+
+    pa_strbuf_puts(buf, "\r\n");
+
+    if (content_type && content) {
+        pa_strbuf_puts(buf, content);
+    }
+
+    /* Our packet is created... now we can send it :) */
+    hdrs = pa_strbuf_tostring_free(buf);
+    /*pa_log_debug("Submitting request:");
+    pa_log_debug(hdrs);*/
+    l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));
+    pa_xfree(hdrs);
+
+    return 0;
+}
+
+
 int pa_rtsp_announce(pa_rtsp_client *c, const char* sdp) {
     pa_assert(c);
     if (!sdp)
diff --git a/src/modules/rtp/rtsp_client.h b/src/modules/rtp/rtsp_client.h
index 5554018..dcc9209 100644
--- a/src/modules/rtp/rtsp_client.h
+++ b/src/modules/rtp/rtsp_client.h
@@ -42,17 +42,17 @@ typedef enum {
   STATE_ANNOUNCE,
   STATE_SETUP,
   STATE_RECORD,
+  STATE_FLUSH,
   STATE_TEARDOWN,
   STATE_SET_PARAMETER,
-  STATE_FLUSH,
   STATE_DISCONNECTED
 } pa_rtsp_state;
 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(const char* useragent);
+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, pa_mainloop_api *mainloop, const char* hostname, uint16_t port);
+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);

commit c49be7891fac98056010cf553042946740061590
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Tue Jun 10 23:55:58 2008 +0000

    Add some new public API functions to connect and flush.
    
    This allows us to reconnect upon disconnection but this has thus far proved unreliable.
    We no longer close the socket. We leave this to the module thread to do the closing.
    We can also flush the remote buffer now.
    Refs #69
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2503 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index 4714d27..48deff0 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -336,22 +336,30 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* he
             break;
         }
 
+        case STATE_FLUSH:
+            pa_log_debug("RAOP: FLUSHED");
+            break;
+
         case STATE_TEARDOWN:
         case STATE_SET_PARAMETER:
-        case STATE_FLUSH:
             break;
         case STATE_DISCONNECTED:
             pa_assert(c->closed_callback);
-            pa_log_debug("RTSP channel closed");
+            pa_assert(c->rtsp);
+
+            pa_log_debug("RTSP control channel closed");
+            pa_rtsp_client_free(c->rtsp);
             c->rtsp = NULL;
             if (c->fd > 0) {
-                pa_close(c->fd);
+                /* We do not close the fd, we leave it to the closed callback to do that */
                 c->fd = -1;
             }
             if (c->sc) {
                 pa_socket_client_unref(c->sc);
                 c->sc = NULL;
             }
+            pa_xfree(c->sid);
+            c->sid = NULL;
             c->closed_callback(c->closed_userdata);
             break;
     }
@@ -359,12 +367,6 @@ 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)
 {
-    char *sci;
-    struct {
-        uint32_t a;
-        uint32_t b;
-        uint32_t c;
-    } rand_data;
     pa_raop_client* c = pa_xnew0(pa_raop_client, 1);
 
     pa_assert(core);
@@ -373,7 +375,43 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char* host)
     c->core = core;
     c->fd = -1;
     c->host = pa_xstrdup(host);
-    c->rtsp = pa_rtsp_client_new("iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
+
+    if (pa_raop_connect(c)) {
+        pa_raop_client_free(c);
+        return NULL;
+    }
+    return c;
+}
+
+
+void pa_raop_client_free(pa_raop_client* c)
+{
+    pa_assert(c);
+
+    if (c->rtsp)
+        pa_rtsp_client_free(c->rtsp);
+    pa_xfree(c->host);
+    pa_xfree(c);
+}
+
+
+int pa_raop_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;
+    }
+
+    c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, 5000, "iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
 
     /* Initialise the AES encryption system */
     pa_random(c->aes_iv, sizeof(c->aes_iv));
@@ -386,23 +424,18 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char* host)
     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);
-    if (pa_rtsp_connect(c->rtsp, c->core->mainloop, host, 5000)) {
-        pa_rtsp_client_free(c->rtsp);
-        return NULL;
-    }
-    return c;
+    return pa_rtsp_connect(c->rtsp);
 }
 
 
-void pa_raop_client_free(pa_raop_client* c)
+int pa_raop_flush(pa_raop_client* c)
 {
     pa_assert(c);
 
-    if (c->rtsp)
-        pa_rtsp_client_free(c->rtsp);
-    pa_xfree(c->host);
-    pa_xfree(c);
+    pa_rtsp_flush(c->rtsp, c->seq, c->rtptime);
+    return 0;
 }
 
 
diff --git a/src/modules/rtp/raop_client.h b/src/modules/rtp/raop_client.h
index 303fdaa..882dae1 100644
--- a/src/modules/rtp/raop_client.h
+++ b/src/modules/rtp/raop_client.h
@@ -33,6 +33,9 @@ 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);
 
+int pa_raop_connect(pa_raop_client* c);
+int pa_raop_flush(pa_raop_client* c);
+
 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);

commit 15e8420a251ec4912d2df9d29dfb728bdc97f33c
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Wed Jun 11 00:02:10 2008 +0000

    Still send silence when we are not doing anything else, but also flush the buffers correctly upon recovery from suspension.
    
    Close the RTP socket correctly after passing messages about.
    When not sending silence, the RTSP socket will be closed after some period of inactivity. I'm not sure why this is.
    Sending silence keeps things working and with the flushes after suspension we now get a better latency. As this relies on the auto-suspend feature, it's not exactly ideal.
    Typical latencies are currently about 3s which makes it more or less usuable for listening to music.
    If the connection is disconnected, it will reconnect but I've found that the second connection is silent. Hopefully the silence will prevent the first connection dropping.
    Refs #69
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2504 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 96c98a6..51c2368 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -127,9 +127,31 @@ static const char* const valid_modargs[] = {
 };
 
 enum {
-    SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX
+    SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX,
+    SINK_MESSAGE_RIP_SOCKET
 };
 
+static void on_connection(PA_GCC_UNUSED int fd, void*userdata) {
+    struct userdata *u = userdata;
+    pa_assert(u);
+
+    pa_assert(u->fd < 0);
+    u->fd = fd;
+
+    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);
+}
+
+static void 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);
+}
+
 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;
 
@@ -143,14 +165,27 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
                     pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
 
                     pa_smoother_pause(u->smoother, pa_rtclock_usec());
+
+                    /* Issue a FLUSH if we are connected */
+                    if (u->fd >= 0) {
+                        pa_raop_flush(u->raop);
+                    }
                     break;
 
                 case PA_SINK_IDLE:
                 case PA_SINK_RUNNING:
 
-                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
+                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
                         pa_smoother_resume(u->smoother, pa_rtclock_usec());
 
+                        /* 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);
+                        else
+                            pa_raop_flush(u->raop);
+                    }
+
                     break;
 
                 case PA_SINK_UNLINKED:
@@ -179,8 +214,34 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
             pollfd->fd = u->fd;
             pollfd->events = POLLOUT;
-            pollfd->revents = 0;
+            /*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);
+            }
+            return 0;
+        }
+
+        case SINK_MESSAGE_RIP_SOCKET: {
+            pa_assert(u->fd >= 0);
 
+            pa_close(u->fd);
+            u->fd = -1;
+
+            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 {
+                /* Quesiton: 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);
+            }
             return 0;
         }
     }
@@ -215,7 +276,7 @@ static void thread_func(void *userdata) {
             pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
 
             /* Render some data and write it to the fifo */
-            if (pollfd->revents) {
+            if (/*PA_SINK_OPENED(u->sink->thread_info.state) && */pollfd->revents) {
                 pa_usec_t usec;
                 int64_t n;
                 void *p;
@@ -264,9 +325,10 @@ static void thread_func(void *userdata) {
                             u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length);
                         } else {
                             /* We render some silence into our memchunk */
-                            u->encoding_overhead += u->next_encoding_overhead;
                             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;
                         }
@@ -302,12 +364,15 @@ static void thread_func(void *userdata) {
 
                         pollfd->revents = 0;
 
-                        if (u->encoded_memchunk.length > 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;
+                        }
                     }
                 }
 
@@ -338,7 +403,7 @@ static void thread_func(void *userdata) {
             }
 
             /* Hmm, nothing to do. Let's sleep */
-            /* pollfd->events = PA_SINK_OPENED(u->sink->thread_info.state)  ? POLLOUT : 0; */
+            pollfd->events = POLLOUT; /*PA_SINK_OPENED(u->sink->thread_info.state)  ? POLLOUT : 0;*/
         }
 
         if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
@@ -353,8 +418,16 @@ static void thread_func(void *userdata) {
             pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
 
             if (pollfd->revents & ~POLLOUT) {
-                pa_log("FIFO shutdown.");
-                goto fail;
+                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;
             }
         }
     }
@@ -371,26 +444,6 @@ finish:
     pa_log_debug("Thread shutting down");
 }
 
-static void on_connection(PA_GCC_UNUSED int fd, void*userdata) {
-    struct userdata *u = userdata;
-    pa_assert(u);
-
-    pa_assert(u->fd < 0);
-    u->fd = fd;
-
-    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);
-}
-
-static void on_close(void*userdata) {
-    struct userdata *u = userdata;
-    pa_assert(u);
-
-    pa_log_debug("Control connection closed.");
-    pa_module_unload_request(u->module);
-}
-
 int pa__init(pa_module*m) {
     struct userdata *u = NULL;
     const char *p;

commit d997420791cb8defbda65acdf08fe18072d01d7b
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Wed Jun 11 22:43:27 2008 +0000

    Minor correction of help text
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2518 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 51c2368..545266d 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -73,7 +73,7 @@ PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(FALSE);
 PA_MODULE_USAGE(
         "sink_name=<name for the sink> "
-        "server=<address> cookie=<filename>  "
+        "server=<address>  "
         "format=<sample format> "
         "channels=<number of channels> "
         "rate=<sample rate>");

commit 729bbaf88485207c8637f3c30fffed60a1f73ca1
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Wed Jun 11 22:44:09 2008 +0000

    Automatic discovery of airtunes devices via Bonjour/Avahi.
    
    This also does some minor reordering in the Makefile.am
    Refs #69
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2519 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/Makefile.am b/src/Makefile.am
index 5a9b902..4ffdfd9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1127,7 +1127,8 @@ endif
 if HAVE_AVAHI
 modlibexec_LTLIBRARIES += \
 		module-zeroconf-publish.la \
-		module-zeroconf-discover.la
+		module-zeroconf-discover.la \
+		module-raop-discover.la
 endif
 
 if HAVE_LIRC
@@ -1207,7 +1208,6 @@ SYMDEF_FILES = \
 		modules/module-esound-compat-spawnfd-symdef.h \
 		modules/module-esound-compat-spawnpid-symdef.h \
 		modules/module-match-symdef.h \
-		modules/module-raop-sink-symdef.h \
 		modules/module-tunnel-sink-symdef.h \
 		modules/module-tunnel-source-symdef.h \
 		modules/module-null-sink-symdef.h \
@@ -1242,6 +1242,8 @@ SYMDEF_FILES = \
 		modules/bluetooth/module-bluetooth-proximity-symdef.h \
 		modules/bluetooth/module-bluetooth-discover-symdef.h \
 		modules/bluetooth/module-bluetooth-device-symdef.h \
+		modules/module-raop-sink-symdef.h \
+		modules/module-raop-discover-symdef.h \
 		modules/gconf/module-gconf-symdef.h \
 		modules/module-position-event-sounds-symdef.h \
 		modules/module-console-kit-symdef.h
@@ -1376,10 +1378,6 @@ module_match_la_SOURCES = modules/module-match.c
 module_match_la_LDFLAGS = -module -avoid-version
 module_match_la_LIBADD = $(AM_LIBADD) libpulsecore.la
 
-module_raop_sink_la_SOURCES = modules/module-raop-sink.c
-module_raop_sink_la_LDFLAGS = -module -avoid-version
-module_raop_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la librtp.la
-
 module_tunnel_sink_la_SOURCES = modules/module-tunnel.c
 module_tunnel_sink_la_CFLAGS = -DTUNNEL_SINK=1 $(AM_CFLAGS)
 module_tunnel_sink_la_LDFLAGS = -module -avoid-version
@@ -1608,6 +1606,17 @@ module_bluetooth_device_la_LDFLAGS = -module -avoid-version
 module_bluetooth_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la libbluetooth-ipc.la libbluetooth-sbc.la libsocket-util.la
 module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
 
+# Apple Airtunes/RAOP
+module_raop_sink_la_SOURCES = modules/module-raop-sink.c
+module_raop_sink_la_LDFLAGS = -module -avoid-version
+module_raop_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la librtp.la
+
+module_raop_discover_la_SOURCES = modules/module-raop-discover.c
+module_raop_discover_la_LDFLAGS = -module -avoid-version
+module_raop_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore.la
+module_raop_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
+
+
 ###################################
 #        Some minor stuff         #
 ###################################
diff --git a/src/modules/module-raop-discover.c b/src/modules/module-raop-discover.c
new file mode 100644
index 0000000..7f62361
--- /dev/null
+++ b/src/modules/module-raop-discover.c
@@ -0,0 +1,380 @@
+/* $Id$ */
+
+/***
+  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 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/avahi-wrap.h>
+
+#include "module-raop-discover-symdef.h"
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Discovery of Airtunes");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define SERVICE_TYPE_SINK "_raop._tcp"
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct tunnel {
+    AvahiIfIndex interface;
+    AvahiProtocol protocol;
+    char *name, *type, *domain;
+    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;
+
+    return
+        (unsigned) t->interface +
+        (unsigned) t->protocol +
+        pa_idxset_string_hash_func(t->name) +
+        pa_idxset_string_hash_func(t->type) +
+        pa_idxset_string_hash_func(t->domain);
+}
+
+static int tunnel_compare(const void *a, const void *b) {
+    const struct tunnel *ta = a, *tb = b;
+    int r;
+
+    if (ta->interface != tb->interface)
+        return 1;
+    if (ta->protocol != tb->protocol)
+        return 1;
+    if ((r = strcmp(ta->name, tb->name)))
+        return r;
+    if ((r = strcmp(ta->type, tb->type)))
+        return r;
+    if ((r = strcmp(ta->domain, tb->domain)))
+        return r;
+
+    return 0;
+}
+
+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;
+    t->name = pa_xstrdup(name);
+    t->type = pa_xstrdup(type);
+    t->domain = pa_xstrdup(domain);
+    t->module_index = PA_IDXSET_INVALID;
+    return t;
+}
+
+static void tunnel_free(struct tunnel *t) {
+    pa_assert(t);
+    pa_xfree(t->name);
+    pa_xfree(t->type);
+    pa_xfree(t->domain);
+    pa_xfree(t);
+}
+
+static void resolver_cb(
+        AvahiServiceResolver *r,
+        AvahiIfIndex interface, AvahiProtocol protocol,
+        AvahiResolverEvent event,
+        const char *name, const char *type, const char *domain,
+        const char *host_name, const AvahiAddress *a, uint16_t port,
+        AvahiStringList *txt,
+        AvahiLookupResultFlags flags,
+        void *userdata) {
+
+    struct userdata *u = userdata;
+    struct tunnel *tnl;
+
+    pa_assert(u);
+
+    tnl = tunnel_new(interface, protocol, name, type, domain);
+
+    if (event != AVAHI_RESOLVER_FOUND)
+        pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client)));
+    else {
+        char *device = NULL, *dname, *args;
+        char at[AVAHI_ADDRESS_STR_MAX];
+        AvahiStringList *l;
+        pa_module *m;
+
+        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 (strcmp(key, "device") == 0) {
+                pa_xfree(device);
+                device = value;
+                value = NULL;
+            }
+            avahi_free(key);
+            avahi_free(value);
+        }
+
+        if (device)
+            dname = pa_sprintf_malloc("airtunes.%s.%s", host_name, device);
+        else
+            dname = pa_sprintf_malloc("airtunes.%s", host_name);
+
+        if (!pa_namereg_is_valid_name(dname)) {
+            pa_log("Cannot construct valid device name from credentials of service '%s'.", dname);
+            avahi_free(device);
+            pa_xfree(dname);
+            goto finish;
+        }
+
+        /*
+         TODO: allow this syntax of server name in things....
+        args = pa_sprintf_malloc("server=[%s]:%u "
+                                 "sink_name=%s",
+                                 avahi_address_snprint(at, sizeof(at), a), port,
+                                 dname);*/
+        args = pa_sprintf_malloc("server=%s "
+                                 "sink_name=%s",
+                                 avahi_address_snprint(at, sizeof(at), a),
+                                 dname);
+
+        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(dname);
+        pa_xfree(args);
+        avahi_free(device);
+    }
+
+finish:
+
+    avahi_service_resolver_free(r);
+
+    if (tnl)
+        tunnel_free(tnl);
+}
+
+static void browser_cb(
+        AvahiServiceBrowser *b,
+        AvahiIfIndex interface, AvahiProtocol protocol,
+        AvahiBrowserEvent event,
+        const char *name, const char *type, const char *domain,
+        AvahiLookupResultFlags flags,
+        void *userdata) {
+
+    struct userdata *u = userdata;
+    struct tunnel *t;
+
+    pa_assert(u);
+
+    if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
+        return;
+
+    t = tunnel_new(interface, protocol, name, type, domain);
+
+    if (event == AVAHI_BROWSER_NEW) {
+
+        if (!pa_hashmap_get(u->tunnels, t))
+            if (!(avahi_service_resolver_new(u->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolver_cb, u)))
+                pa_log("avahi_service_resolver_new() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
+
+        /* We ignore the returned resolver object here, since the we don't
+         * need to attach any special data to it, and we can still destory
+         * it from the callback */
+
+    } else if (event == AVAHI_BROWSER_REMOVE) {
+        struct tunnel *t2;
+
+        if ((t2 = pa_hashmap_get(u->tunnels, t))) {
+            pa_module_unload_by_index(u->core, t2->module_index);
+            pa_hashmap_remove(u->tunnels, t2);
+            tunnel_free(t2);
+        }
+    }
+
+    tunnel_free(t);
+}
+
+static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    u->client = c;
+
+    switch (state) {
+        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,
+                              SERVICE_TYPE_SINK,
+                              NULL,
+                              0,
+                              browser_cb, u))) {
+
+                    pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c)));
+                    pa_module_unload_request(u->module);
+                }
+            }
+
+            break;
+
+        case AVAHI_CLIENT_FAILURE:
+            if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
+                int error;
+
+                pa_log_debug("Avahi daemon disconnected.");
+
+                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);
+                }
+            }
+
+            /* Fall through */
+
+        case AVAHI_CLIENT_CONNECTING:
+
+            if (u->sink_browser) {
+                avahi_service_browser_free(u->sink_browser);
+                u->sink_browser = NULL;
+            }
+
+            break;
+
+        default: ;
+    }
+}
+
+int pa__init(pa_module*m) {
+
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    int error;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->sink_browser = NULL;
+
+    u->tunnels = pa_hashmap_new(tunnel_hash, tunnel_compare);
+
+    u->avahi_poll = pa_avahi_poll_new(m->core->mainloop);
+
+    if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+        pa_log("pa_avahi_client_new() failed: %s", avahi_strerror(error));
+        goto fail;
+    }
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata*u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->client)
+        avahi_client_free(u->client);
+
+    if (u->avahi_poll)
+        pa_avahi_poll_free(u->avahi_poll);
+
+    if (u->tunnels) {
+        struct tunnel *t;
+
+        while ((t = pa_hashmap_steal_first(u->tunnels))) {
+            pa_module_unload_by_index(u->core, t->module_index);
+            tunnel_free(t);
+        }
+
+        pa_hashmap_free(u->tunnels, NULL, NULL);
+    }
+
+    pa_xfree(u);
+}

commit 0ff75aea053cb8a7af958aa72d99b27cc90becf6
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Wed Jun 11 23:01:07 2008 +0000

    Add Lennart back in to Copyright as I copied these files from his originals and was a bit overzealous in changing things ;)
    
    git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2520 fefdeb5f-60dc-0310-8127-8f9354f1896f

diff --git a/src/modules/module-raop-discover.c b/src/modules/module-raop-discover.c
index 7f62361..7f89bb5 100644
--- a/src/modules/module-raop-discover.c
+++ b/src/modules/module-raop-discover.c
@@ -3,6 +3,7 @@
 /***
   This file is part of PulseAudio.
 
+  Copyright 2004-2006 Lennart Poettering
   Copyright 2008 Colin Guthrie
 
   PulseAudio is free software; you can redistribute it and/or modify
diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 545266d..39374a3 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -3,6 +3,7 @@
 /***
   This file is part of PulseAudio.
 
+  Copyright 2004-2006 Lennart Poettering
   Copyright 2008 Colin Guthrie
 
   PulseAudio is free software; you can redistribute it and/or modify

commit 36f2aad5f0892e6738a8d59111c3c9017152db7e
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Tue Jun 24 23:57:37 2008 +0100

    Use the new pa_namereg_make_valid_name() function.

diff --git a/src/modules/module-raop-discover.c b/src/modules/module-raop-discover.c
index 7f89bb5..baa64ee 100644
--- a/src/modules/module-raop-discover.c
+++ b/src/modules/module-raop-discover.c
@@ -154,7 +154,7 @@ static void resolver_cb(
     if (event != AVAHI_RESOLVER_FOUND)
         pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client)));
     else {
-        char *device = NULL, *dname, *args;
+        char *device = NULL, *dname, *vname, *args;
         char at[AVAHI_ADDRESS_STR_MAX];
         AvahiStringList *l;
         pa_module *m;
@@ -178,23 +178,24 @@ static void resolver_cb(
         else
             dname = pa_sprintf_malloc("airtunes.%s", host_name);
 
-        if (!pa_namereg_is_valid_name(dname)) {
-            pa_log("Cannot construct valid device name from credentials of service '%s'.", dname);
+        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;
         }
+        pa_xfree(dname);
 
         /*
          TODO: allow this syntax of server name in things....
         args = pa_sprintf_malloc("server=[%s]:%u "
                                  "sink_name=%s",
                                  avahi_address_snprint(at, sizeof(at), a), port,
-                                 dname);*/
+                                 vname);*/
         args = pa_sprintf_malloc("server=%s "
                                  "sink_name=%s",
                                  avahi_address_snprint(at, sizeof(at), a),
-                                 dname);
+                                 vname);
 
         pa_log_debug("Loading module-raop-sink with arguments '%s'", args);
 
@@ -204,7 +205,7 @@ static void resolver_cb(
             tnl = NULL;
         }
 
-        pa_xfree(dname);
+        pa_xfree(vname);
         pa_xfree(args);
         avahi_free(device);
     }

commit e543e04ca725ef1c240762529e7dafec31749683
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Thu Jul 3 23:47:34 2008 +0100

    Implement a set volume function to expose this capability to higher layers

diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index 48deff0..792ecee 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -342,6 +342,7 @@ static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* he
 
         case STATE_TEARDOWN:
         case STATE_SET_PARAMETER:
+            pa_log_debug("RAOP: SET_PARAMETER");
             break;
         case STATE_DISCONNECTED:
             pa_assert(c->closed_callback);
@@ -439,6 +440,29 @@ int pa_raop_flush(pa_raop_client* c)
 }
 
 
+int pa_raop_client_set_volume(pa_raop_client* c, pa_volume_t volume)
+{
+    int rv;
+    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;
+
+    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);
+    pa_xfree(param);
+    return rv;
+}
+
+
 int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchunk* encoded)
 {
     uint16_t len;
diff --git a/src/modules/rtp/raop_client.h b/src/modules/rtp/raop_client.h
index 882dae1..3d5ef16 100644
--- a/src/modules/rtp/raop_client.h
+++ b/src/modules/rtp/raop_client.h
@@ -36,6 +36,7 @@ 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_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);

commit ded09d1f9b00ac793ccc26caea0f8706e88bd521
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Thu Jul 3 23:49:01 2008 +0100

    Implement hardware volume control.
    This allows near instant change of volume when controlling the hardware but the stream volume still suffers from a sizable delay.

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 39374a3..50ef985 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -102,6 +102,9 @@ struct userdata {
 
     pa_usec_t latency;
 
+    pa_volume_t volume;
+    pa_bool_t muted;
+
     /*esd_format_t format;*/
     int32_t rate;
 
@@ -139,6 +142,9 @@ static void on_connection(PA_GCC_UNUSED int fd, void*userdata) {
     pa_assert(u->fd < 0);
     u->fd = fd;
 
+    /* Set the initial volume */
+    pa_raop_client_set_volume(u->raop, u->volume);
+
     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);
@@ -250,12 +256,68 @@ 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 int sink_get_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    int i;
+
+    pa_assert(u);
+
+    for (i = 0; i < s->sample_spec.channels; i++) {
+        s->volume.values[i] = u->volume;
+    }
+
+    return 0;
+}
+
+static int sink_set_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    int rv;
+
+    pa_assert(u);
+
+    /* If we're muted, we fake it */
+    if (u->muted)
+        return 0;
+
+    pa_assert(s->sample_spec.channels > 0);
+
+    /* Avoid pointless volume sets */
+    if (u->volume == s->volume.values[0])
+        return 0;
+
+    rv = pa_raop_client_set_volume(u->raop, s->volume.values[0]);
+    if (0 == rv)
+        u->volume = s->volume.values[0];
+
+    return rv;
+}
+
+static int sink_get_mute_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(u);
+
+    s->muted = u->muted;
+    return 0;
+}
+
+static int sink_set_mute_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    int rv;
+
+    pa_assert(u);
+
+    rv = pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume));
+    u->muted = s->muted;
+    return rv;
+}
+
 static void thread_func(void *userdata) {
     struct userdata *u = userdata;
     int write_type = 0;
     pa_memchunk silence;
-    uint32_t silence_overhead;
-    double silence_ratio;
+    uint32_t silence_overhead = 0;
+    double silence_ratio = 0;
 
     pa_assert(u);
 
@@ -484,6 +546,9 @@ int pa__init(pa_module*m) {
     u->next_encoding_overhead = 0;
     u->encoding_ratio = 1.0;
 
+    u->volume = roundf(0.7 * PA_VOLUME_NORM);
+    u->muted = FALSE;
+
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
     u->rtpoll = pa_rtpoll_new();
     pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
@@ -508,7 +573,11 @@ int pa__init(pa_module*m) {
 
     u->sink->parent.process_msg = sink_process_msg;
     u->sink->userdata = u;
-    u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK;
+    u->sink->get_volume = sink_get_volume_cb;
+    u->sink->set_volume = sink_set_volume_cb;
+    u->sink->get_mute = sink_get_mute_cb;
+    u->sink->set_mute = sink_set_mute_cb;
+    u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK|PA_SINK_HW_VOLUME_CTRL;
 
     pa_sink_set_module(u->sink, m);
     pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);

commit 19d28319733f92cc518d0d45a15f15b704f603d8
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun Aug 3 20:52:35 2008 +0100

    Make module-raop-sink/discover work with 0.9.11 API

diff --git a/src/modules/module-raop-discover.c b/src/modules/module-raop-discover.c
index baa64ee..38436a3 100644
--- a/src/modules/module-raop-discover.c
+++ b/src/modules/module-raop-discover.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 50ef985..b90d4e2 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
@@ -169,7 +167,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
 
                 case PA_SINK_SUSPENDED:
-                    pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
+                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
 
                     pa_smoother_pause(u->smoother, pa_rtclock_usec());
 
@@ -334,12 +332,16 @@ static void thread_func(void *userdata) {
     for (;;) {
         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);
+
         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_OPENED(u->sink->thread_info.state) && */pollfd->revents) {
+            if (/*PA_SINK_IS_OPENED(u->sink->thread_info.state) && */pollfd->revents) {
                 pa_usec_t usec;
                 int64_t n;
                 void *p;
@@ -366,7 +368,7 @@ static void thread_func(void *userdata) {
                     if (u->encoded_memchunk.length <= 0) {
                         if (u->encoded_memchunk.memblock)
                             pa_memblock_unref(u->encoded_memchunk.memblock);
-                        if (PA_SINK_OPENED(u->sink->thread_info.state)) {
+                        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
                             size_t rl;
 
                             /* We render real data */
@@ -466,7 +468,7 @@ static void thread_func(void *userdata) {
             }
 
             /* Hmm, nothing to do. Let's sleep */
-            pollfd->events = POLLOUT; /*PA_SINK_OPENED(u->sink->thread_info.state)  ? POLLOUT : 0;*/
+            pollfd->events = POLLOUT; /*PA_SINK_IS_OPENED(u->sink->thread_info.state)  ? POLLOUT : 0;*/
         }
 
         if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
@@ -509,10 +511,10 @@ finish:
 
 int pa__init(pa_module*m) {
     struct userdata *u = NULL;
-    const char *p;
     pa_sample_spec ss;
     pa_modargs *ma = NULL;
-    char *t;
+    const char *server;
+    pa_sink_new_data data;
 
     pa_assert(m);
 
@@ -538,7 +540,7 @@ int pa__init(pa_module*m) {
     u->module = m;
     m->userdata = u;
     u->fd = -1;
-    u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE);
+    u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
     pa_memchunk_reset(&u->raw_memchunk);
     pa_memchunk_reset(&u->encoded_memchunk);
     u->offset = 0;
@@ -549,9 +551,8 @@ int pa__init(pa_module*m) {
     u->volume = roundf(0.7 * PA_VOLUME_NORM);
     u->muted = FALSE;
 
-    pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
     u->rtpoll = pa_rtpoll_new();
-    pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
     u->rtpoll_item = NULL;
 
     /*u->format =
@@ -566,7 +567,23 @@ int pa__init(pa_module*m) {
     /*u->state = STATE_AUTH;*/
     u->latency = 0;
 
-    if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) {
+    if (!(server = pa_modargs_get_value(ma, "server", NULL))) {
+        pa_log("No server argument given.");
+        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_sample_spec(&data, &ss);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Airtunes sink '%s'", server);
+
+    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.");
         goto fail;
     }
@@ -579,25 +596,16 @@ int pa__init(pa_module*m) {
     u->sink->set_mute = sink_set_mute_cb;
     u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK|PA_SINK_HW_VOLUME_CTRL;
 
-    pa_sink_set_module(u->sink, m);
     pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
     pa_sink_set_rtpoll(u->sink, u->rtpoll);
 
-    if (!(p = pa_modargs_get_value(ma, "server", NULL))) {
-        pa_log("No server argument given.");
-        goto fail;
-    }
-
-    if (!(u->raop = pa_raop_client_new(u->core, p))) {
+    if (!(u->raop = pa_raop_client_new(u->core, server))) {
         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);
-    pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Airtunes sink '%s'", p));
-    pa_xfree(t);
-
 
     if (!(u->thread = pa_thread_new(thread_func, u))) {
         pa_log("Failed to create thread.");

commit c3d8bb5b34c45f4dda594cc1d8107cac468fa232
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun Aug 3 20:56:21 2008 +0100

    Remove $Id$ lines left over from SVN

diff --git a/src/modules/rtp/base64.c b/src/modules/rtp/base64.c
index 980b018..8918def 100644
--- a/src/modules/rtp/base64.c
+++ b/src/modules/rtp/base64.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
diff --git a/src/modules/rtp/base64.h b/src/modules/rtp/base64.h
index 347a997..dac0e70 100644
--- a/src/modules/rtp/base64.h
+++ b/src/modules/rtp/base64.h
@@ -1,8 +1,6 @@
 #ifndef foobase64hfoo
 #define foobase64hfoo
 
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
diff --git a/src/modules/rtp/headerlist.c b/src/modules/rtp/headerlist.c
index de8710b..0fef835 100644
--- a/src/modules/rtp/headerlist.c
+++ b/src/modules/rtp/headerlist.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
diff --git a/src/modules/rtp/headerlist.h b/src/modules/rtp/headerlist.h
index 276d0e3..4b9c643 100644
--- a/src/modules/rtp/headerlist.h
+++ b/src/modules/rtp/headerlist.h
@@ -1,8 +1,6 @@
 #ifndef foopulseheaderlisthfoo
 #define foopulseheaderlisthfoo
 
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c
index 792ecee..4627545 100644
--- a/src/modules/rtp/raop_client.c
+++ b/src/modules/rtp/raop_client.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
diff --git a/src/modules/rtp/raop_client.h b/src/modules/rtp/raop_client.h
index 3d5ef16..ec3136a 100644
--- a/src/modules/rtp/raop_client.h
+++ b/src/modules/rtp/raop_client.h
@@ -1,8 +1,6 @@
 #ifndef fooraopclientfoo
 #define fooraopclientfoo
 
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index 88f7781..9eb3d96 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
diff --git a/src/modules/rtp/rtsp_client.h b/src/modules/rtp/rtsp_client.h
index dcc9209..88fb383 100644
--- a/src/modules/rtp/rtsp_client.h
+++ b/src/modules/rtp/rtsp_client.h
@@ -1,8 +1,6 @@
 #ifndef foortspclienthfoo
 #define foortspclienthfoo
 
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 

commit 8715121755ad1aa9c083dd70781a63ced13359c2
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Sun Aug 3 22:46:21 2008 +0100

    Modularise the RAOP stuff that requires OpenSSL and make it optional at compile time

diff --git a/configure.ac b/configure.ac
index 2b91a00..2a04076 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1000,6 +1000,41 @@ AC_SUBST(POLKIT_LIBS)
 AC_SUBST(HAVE_POLKIT)
 AM_CONDITIONAL([HAVE_POLKIT], [test "x$HAVE_POLKIT" = x1])
 
+#### OpenSSL support (optional) ####
+
+AC_ARG_ENABLE([openssl],
+    AC_HELP_STRING([--disable-openssl], [Disable OpenSSL support (used for Airtunes/RAOP)]),
+        [
+            case "${enableval}" in
+                yes) openssl=yes ;;
+                no) openssl=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-openssl) ;;
+            esac
+        ],
+        [openssl=auto])
+
+if test "x${openssl}" != xno ; then
+
+    PKG_CHECK_MODULES(OPENSSL, [ openssl > 0.9 ],
+        [
+            HAVE_OPENSSL=1
+            AC_DEFINE([HAVE_OPENSSL], 1, [Have OpenSSL])
+        ],
+        [
+            HAVE_OPENSSL=0
+            if test "x$openssl" = xyes ; then
+                AC_MSG_ERROR([*** OpenSSL support not found])
+            fi
+        ])
+else
+    HAVE_OPENSSL=0
+fi
+
+AC_SUBST(OPENSSL_CFLAGS)
+AC_SUBST(OPENSSL_LIBS)
+AC_SUBST(HAVE_OPENSSL)
+AM_CONDITIONAL([HAVE_OPENSSL], [test "x$HAVE_OPENSSL" = x1])
+
 ### Build and Install man pages ###
 AC_ARG_ENABLE(manpages,
         AS_HELP_STRING([--disable-manpages],[Disable building and installation of man pages]),
@@ -1201,6 +1236,11 @@ if test "x${HAVE_POLKIT}" = "x1" ; then
    ENABLE_POLKIT=yes
 fi
 
+ENABLE_OPENSSL=no
+if test "x${HAVE_OPENSSL}" = "x1" ; then
+   ENABLE_OPENSSL=yes
+fi
+
 ENABLE_PER_USER_ESOUND_SOCKET=no
 if test "x$per_user_esound_socket" = "x1" ; then
    ENABLE_PER_USER_ESOUND_SOCKET=yes
@@ -1232,6 +1272,7 @@ echo "
     Enable TCP Wrappers:           ${ENABLE_TCPWRAP}
     Enable libsamplerate:          ${ENABLE_LIBSAMPLERATE}
     Enable PolicyKit:              ${ENABLE_POLKIT}
+    Enable OpenSSL (for Airtunes): ${ENABLE_OPENSSL}
     System User:                   ${PA_SYSTEM_USER}
     System Group:                  ${PA_SYSTEM_GROUP}
     Realtime Group:                ${PA_REALTIME_GROUP}
diff --git a/src/Makefile.am b/src/Makefile.am
index 4ffdfd9..f216b71 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -62,6 +62,10 @@ AM_CFLAGS += -DPA_MACHINE_ID=\"$(localstatedir)/lib/dbus/machine-id\"
 # This cool debug trap works on i386/gcc only
 AM_CFLAGS += '-DDEBUG_TRAP=__asm__("int $$3")'
 
+if HAVE_OPENSSL
+AM_CFLAGS += -I$(top_builddir)/src/modules/raop
+endif
+
 AM_LIBADD = $(PTHREAD_LIBS) $(INTLLIBS)
 AM_LDADD = $(PTHREAD_LIBS) $(INTLLIBS)
 
@@ -89,6 +93,7 @@ PA_THREAD_OBJS = \
 		pulsecore/semaphore-posix.c pulsecore/semaphore.h
 endif
 
+
 ###################################
 #          Extra files            #
 ###################################
@@ -1009,11 +1014,16 @@ librtp_la_SOURCES = \
 		modules/rtp/sdp.c modules/rtp/sdp.h \
 		modules/rtp/sap.c modules/rtp/sap.h \
 		modules/rtp/rtsp_client.c modules/rtp/rtsp_client.h \
-		modules/rtp/raop_client.c modules/rtp/raop_client.h \
-		modules/rtp/headerlist.c modules/rtp/headerlist.h \
-		modules/rtp/base64.c modules/rtp/base64.h
+		modules/rtp/headerlist.c modules/rtp/headerlist.h
 librtp_la_LDFLAGS = -avoid-version
-librtp_la_LIBADD = $(AM_LIBADD) libsocket-util.la libiochannel.la libsocket-client.la libioline.la libpulsecore.la -lssl
+librtp_la_LIBADD = $(AM_LIBADD) libsocket-util.la libiochannel.la libsocket-client.la libioline.la libpulsecore.la
+
+libraop_la_SOURCES = \
+        modules/raop/raop_client.c modules/raop/raop_client.h \
+        modules/raop/base64.c modules/raop/base64.h
+libraop_la_CFLAGS = $(AM_CFLAGS) $(OPENSSL_CFLAGS)
+libraop_la_LDFLAGS = -avoid-version
+libraop_la_LIBADD = $(AM_LIBADD) $(OPENSSL_LIBS) libsocket-util.la libiochannel.la libsocket-client.la libioline.la libpulsecore.la librtp.la
 
 # X11
 
@@ -1060,7 +1070,6 @@ modlibexec_LTLIBRARIES += \
 		module-remap-sink.la \
 		module-ladspa-sink.la \
 		module-esound-sink.la \
-		module-raop-sink.la \
 		module-tunnel-sink.la \
 		module-tunnel-source.la \
 		module-position-event-sounds.la
@@ -1127,8 +1136,7 @@ endif
 if HAVE_AVAHI
 modlibexec_LTLIBRARIES += \
 		module-zeroconf-publish.la \
-		module-zeroconf-discover.la \
-		module-raop-discover.la
+		module-zeroconf-discover.la
 endif
 
 if HAVE_LIRC
@@ -1186,6 +1194,17 @@ pulselibexec_PROGRAMS += \
 		proximity-helper
 endif
 
+if HAVE_OPENSSL
+modlibexec_LTLIBRARIES += \
+		libraop.la \
+		module-raop-sink.la
+if HAVE_AVAHI
+modlibexec_LTLIBRARIES += \
+		module-raop-discover.la
+endif
+endif
+
+
 # These are generated by a M4 script
 
 SYMDEF_FILES = \
@@ -1609,7 +1628,7 @@ module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
 # Apple Airtunes/RAOP
 module_raop_sink_la_SOURCES = modules/module-raop-sink.c
 module_raop_sink_la_LDFLAGS = -module -avoid-version
-module_raop_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la librtp.la
+module_raop_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la librtp.la libraop.la
 
 module_raop_discover_la_SOURCES = modules/module-raop-discover.c
 module_raop_discover_la_LDFLAGS = -module -avoid-version
diff --git a/src/modules/rtp/base64.c b/src/modules/raop/base64.c
similarity index 100%
rename from src/modules/rtp/base64.c
rename to src/modules/raop/base64.c
diff --git a/src/modules/rtp/base64.h b/src/modules/raop/base64.h
similarity index 100%
rename from src/modules/rtp/base64.h
rename to src/modules/raop/base64.h
diff --git a/src/modules/rtp/raop_client.c b/src/modules/raop/raop_client.c
similarity index 100%
rename from src/modules/rtp/raop_client.c
rename to src/modules/raop/raop_client.c
diff --git a/src/modules/rtp/raop_client.h b/src/modules/raop/raop_client.h
similarity index 100%
rename from src/modules/rtp/raop_client.h
rename to src/modules/raop/raop_client.h

commit 59eb64987fdf5dde71638f5f77e705e490ec7133
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Fri Aug 22 09:51:41 2008 +0100

    Follow master change r34dd4a and fix shutdown when --disallow-module-loading=1 is passed

diff --git a/src/modules/module-raop-discover.c b/src/modules/module-raop-discover.c
index 38436a3..3706d92 100644
--- a/src/modules/module-raop-discover.c
+++ b/src/modules/module-raop-discover.c
@@ -248,7 +248,7 @@ static void browser_cb(
         struct tunnel *t2;
 
         if ((t2 = pa_hashmap_get(u->tunnels, t))) {
-            pa_module_unload_by_index(u->core, t2->module_index);
+            pa_module_unload_by_index(u->core, t2->module_index, TRUE);
             pa_hashmap_remove(u->tunnels, t2);
             tunnel_free(t2);
         }
@@ -281,7 +281,7 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
                               browser_cb, u))) {
 
                     pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c)));
-                    pa_module_unload_request(u->module);
+                    pa_module_unload_request(u->module, TRUE);
                 }
             }
 
@@ -295,7 +295,7 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
 
                 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);
+                    pa_module_unload_request(u->module, TRUE);
                 }
             }
 
@@ -369,7 +369,7 @@ void pa__done(pa_module*m) {
         struct tunnel *t;
 
         while ((t = pa_hashmap_steal_first(u->tunnels))) {
-            pa_module_unload_by_index(u->core, t->module_index);
+            pa_module_unload_by_index(u->core, t->module_index, TRUE);
             tunnel_free(t);
         }
 
diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index b90d4e2..62f0a73 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -245,7 +245,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
                 /* Quesiton: 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);
+                pa_module_unload_request(u->module, TRUE);
             }
             return 0;
         }

commit 8e3e88df8b4f9eb82d305e04b23b22574fb7847d
Merge: 91b64bc... 59eb649...
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Oct 8 23:03:16 2008 +0200

    Merge commit 'coling/airtunes-0.9.13'

diff --cc src/Makefile.am
index 7487839,f216b71..4d1812e
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@@ -1234,10 -1261,11 +1262,12 @@@ SYMDEF_FILES = 
  		modules/bluetooth/module-bluetooth-proximity-symdef.h \
  		modules/bluetooth/module-bluetooth-discover-symdef.h \
  		modules/bluetooth/module-bluetooth-device-symdef.h \
+ 		modules/module-raop-sink-symdef.h \
+ 		modules/module-raop-discover-symdef.h \
  		modules/gconf/module-gconf-symdef.h \
  		modules/module-position-event-sounds-symdef.h \
 -		modules/module-console-kit-symdef.h
 +		modules/module-console-kit-symdef.h \
 +		modules/module-flat-volume-symdef.h
  
  EXTRA_DIST += $(SYMDEF_FILES)
  BUILT_SOURCES += $(SYMDEF_FILES)

-- 
hooks/post-receive
PulseAudio Sound Server



More information about the pulseaudio-commits mailing list