[pulseaudio-commits] [SCM] PulseAudio Sound Server branch, master, updated. v0.9.19-330-gaa93484

Lennart Poettering gitmailer-noreply at 0pointer.de
Tue Jan 5 15:22:41 PST 2010


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  6faf38313e5f313cbcfe5c78af1dd310131798d2 (commit)

- Log -----------------------------------------------------------------
aa93484 core: volume ramping fix
6dfb1ee Merge remote branch 'zonque/topic/osx'
937c587 fix a number of warnings
f452c6c fix a number of warnings
0ccc7af osx: add native zeroconf implementation via Bonjour
7732421 CoreAudio: add audio device module
a23f5cf CoreAudio: add device detection module
28a73ad hack around another OS X bug: recv() with MSG_PEEK does not work
17d3446 poll() is totally broken on Mac OS X
962164a src/Makefile.am: add specific OS_IS_DARWIN files
a46ddfe core-rtclock.c: tweak OS_IS_DARWIN constraints
76acaa9 configure.ac: add DARWIN_OS variable
f9b03d3 Merge branch 'master' of git://0pointer.de/pulseaudio
1b2cbe9 Merge branch 'master' of git://0pointer.de/pulseaudio
d5d488e Merge branch 'master' of git://0pointer.de/pulseaudio
ed7642e Merge branch 'master' of git://0pointer.de/pulseaudio
21ffcd5 Merge branch 'master' of git://0pointer.de/pulseaudio
c4e276e Merge branch 'master' of git://0pointer.de/pulseaudio
9c61465 Merge branch 'master' of git://0pointer.de/pulseaudio
e11cd13 Merge branch 'master' of git://0pointer.de/pulseaudio
0b388bf Merge branch 'master' of git://0pointer.de/pulseaudio
94aa909 Merge branch 'master' of git://0pointer.de/pulseaudio
c1e59f7 Merge branch 'master' of git://0pointer.de/pulseaudio
bebaa49 Merge branch 'master' of git://0pointer.de/pulseaudio
c6d330e Merge branch 'osx' of git://gitorious.org/~flameeyes/pulseaudio/flameeyes-pulseaudio
63f2ed6 Merge branch 'osx' of git://gitorious.org/~flameeyes/pulseaudio/flameeyes-pulseaudio
30fcda6 Revert "make bootstrap.sh aware of Darwin environment"
3fc7b87 Merge branch 'osx' of git://gitorious.org/~flameeyes/pulseaudio/flameeyes-pulseaudio
909dc09 Merge branch 'master' of git://gitorious.org/~flameeyes/pulseaudio/flameeyes-pulseaudio
5f5867e make bootstrap.sh aware of Darwin environment
-----------------------------------------------------------------------

Summary of changes:
 configure.ac                                    |   26 +
 src/Makefile.am                                 |   47 ++-
 src/daemon/daemon-conf.c                        |    4 +
 src/modules/coreaudio/module-coreaudio-detect.c |  229 +++++++
 src/modules/coreaudio/module-coreaudio-device.c |  820 +++++++++++++++++++++++
 src/modules/module-bonjour-publish.c            |  516 ++++++++++++++
 src/modules/module-sine-source.c                |    2 +-
 src/modules/raop/module-raop-sink.c             |    2 +-
 src/modules/raop/raop_client.c                  |    5 -
 src/modules/rtp/rtsp_client.c                   |    5 -
 src/pulse/mainloop.c                            |    9 +-
 src/pulsecore/core-rtclock.c                    |   69 +--
 src/pulsecore/core-util.c                       |    4 +-
 src/pulsecore/envelope.c                        |    8 +-
 src/pulsecore/lock-autospawn.c                  |    3 +-
 src/pulsecore/poll.c                            |   33 +-
 src/pulsecore/poll.h                            |   18 +-
 src/pulsecore/rtpoll.c                          |    9 +-
 src/pulsecore/sink-input.c                      |   14 +-
 src/tests/lock-autospawn-test.c                 |    4 +-
 src/tests/rtpoll-test.c                         |    2 +-
 src/utils/pacmd.c                               |    4 +-
 22 files changed, 1737 insertions(+), 96 deletions(-)
 create mode 100644 src/modules/coreaudio/module-coreaudio-detect.c
 create mode 100644 src/modules/coreaudio/module-coreaudio-device.c
 create mode 100644 src/modules/module-bonjour-publish.c

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

commit 5f5867e08ab8821447faacece92dab1e0b993fe4
Author: Daniel Mack <daniel at caiaq.de>
Date:   Fri Jul 17 13:45:37 2009 +0200

    make bootstrap.sh aware of Darwin environment
    
    Signed-off-by: Daniel Mack <daniel at caiaq.de>

diff --git a/bootstrap.sh b/bootstrap.sh
index aa9755f..a5bd9a7 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -40,10 +40,22 @@ run_versioned() {
 
 set -ex
 
+case $(uname) in
+	*Darwin*)
+		CP_OPTS="-R"
+		CHMOD_OPTS=""
+		LIBTOOLIZE="glibtoolize"
+		;;
+	*)
+		CP_OPTS="-av"
+		CHMOD_OPTS="-c"
+		;;
+esac
+
 if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then
     echo "Activating pre-commit hook."
-    cp -av .git/hooks/pre-commit.sample .git/hooks/pre-commit
-    chmod -c +x  .git/hooks/pre-commit
+    cp ${CP_OPTS}  .git/hooks/pre-commit.sample .git/hooks/pre-commit
+    chmod ${CHMOD_OPTS} +x  .git/hooks/pre-commit
 fi
 
 if [ -f .tarball-version ]; then

commit 909dc093a5d33b5eed2c718187c7acce1c6d9bd0
Merge: 5f5867e 673112b
Author: Daniel Mack <daniel at caiaq.de>
Date:   Fri Jul 17 14:20:57 2009 +0200

    Merge branch 'master' of git://gitorious.org/~flameeyes/pulseaudio/flameeyes-pulseaudio


commit 3fc7b870fcd8b3cc7a6b73fd3d4449994860f4fc
Merge: 909dc09 a6d6718
Author: Daniel Mack <daniel at caiaq.de>
Date:   Fri Jul 17 14:34:52 2009 +0200

    Merge branch 'osx' of git://gitorious.org/~flameeyes/pulseaudio/flameeyes-pulseaudio


commit 30fcda6cb5959af2a1911bb40bf769be6b554450
Author: Daniel Mack <daniel at caiaq.de>
Date:   Fri Jul 17 20:52:23 2009 +0200

    Revert "make bootstrap.sh aware of Darwin environment"
    
    This reverts commit 5f5867e08ab8821447faacece92dab1e0b993fe4.

diff --git a/bootstrap.sh b/bootstrap.sh
index a5bd9a7..aa9755f 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -40,22 +40,10 @@ run_versioned() {
 
 set -ex
 
-case $(uname) in
-	*Darwin*)
-		CP_OPTS="-R"
-		CHMOD_OPTS=""
-		LIBTOOLIZE="glibtoolize"
-		;;
-	*)
-		CP_OPTS="-av"
-		CHMOD_OPTS="-c"
-		;;
-esac
-
 if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then
     echo "Activating pre-commit hook."
-    cp ${CP_OPTS}  .git/hooks/pre-commit.sample .git/hooks/pre-commit
-    chmod ${CHMOD_OPTS} +x  .git/hooks/pre-commit
+    cp -av .git/hooks/pre-commit.sample .git/hooks/pre-commit
+    chmod -c +x  .git/hooks/pre-commit
 fi
 
 if [ -f .tarball-version ]; then

commit 63f2ed6dd7b4b87dbe175f8dda35204dee8737e0
Merge: 30fcda6 8c85c99
Author: Daniel Mack <daniel at caiaq.de>
Date:   Fri Jul 17 20:52:34 2009 +0200

    Merge branch 'osx' of git://gitorious.org/~flameeyes/pulseaudio/flameeyes-pulseaudio


commit c6d330ef91ce401660f907536b1bfd0591c14c2a
Merge: 63f2ed6 d18eb61
Author: Daniel Mack <daniel at caiaq.de>
Date:   Fri Jul 17 21:06:58 2009 +0200

    Merge branch 'osx' of git://gitorious.org/~flameeyes/pulseaudio/flameeyes-pulseaudio


commit bebaa491650c48697b861ec5e76816e9af1a8803
Merge: c6d330e 180ef1e
Author: Daniel Mack <daniel at caiaq.de>
Date:   Mon Sep 14 16:25:35 2009 +0800

    Merge branch 'master' of git://0pointer.de/pulseaudio


commit c1e59f7d762fb147bc5250ebddb9cf5639aba522
Merge: bebaa49 12f2111
Author: Daniel Mack <daniel at caiaq.de>
Date:   Tue Sep 15 11:46:19 2009 +0800

    Merge branch 'master' of git://0pointer.de/pulseaudio


commit 94aa9097f4ded68623160d754a4bf2632b8efc79
Merge: c1e59f7 c194db7
Author: Daniel Mack <daniel at caiaq.de>
Date:   Tue Sep 22 11:30:30 2009 +0800

    Merge branch 'master' of git://0pointer.de/pulseaudio


commit 0b388bff522f689fdb4d98529a39f3701db57c08
Merge: 94aa909 8ec304d
Author: Daniel Mack <daniel at caiaq.de>
Date:   Thu Oct 8 14:19:47 2009 +0800

    Merge branch 'master' of git://0pointer.de/pulseaudio


commit e11cd135aa345dc39db16340702bfc8bc2673983
Merge: 0b388bf c9375aa
Author: Daniel Mack <daniel at caiaq.de>
Date:   Tue Oct 13 08:56:49 2009 +0800

    Merge branch 'master' of git://0pointer.de/pulseaudio


commit 9c61465c796f3369c7cc57c094489fb383216a1b
Merge: e11cd13 e9ccc61
Author: Daniel Mack <daniel at caiaq.de>
Date:   Tue Oct 27 13:00:08 2009 +0100

    Merge branch 'master' of git://0pointer.de/pulseaudio


commit c4e276edbd84cbb8c5b594c9f427b0a25a7fb2ab
Merge: 9c61465 2dc37e1
Author: Daniel Mack <daniel at caiaq.de>
Date:   Sat Oct 31 02:16:14 2009 +0100

    Merge branch 'master' of git://0pointer.de/pulseaudio


commit 21ffcd55a5a425870f8f4c3b4933bc7cb352e339
Merge: c4e276e 7938442
Author: Daniel Mack <daniel at caiaq.de>
Date:   Mon Nov 2 18:09:57 2009 +0100

    Merge branch 'master' of git://0pointer.de/pulseaudio


commit ed7642ebac32bde8d9c47f3a1d8eabf75c3ea986
Merge: 21ffcd5 2294642
Author: Daniel Mack <daniel at caiaq.de>
Date:   Wed Nov 11 10:48:39 2009 +0100

    Merge branch 'master' of git://0pointer.de/pulseaudio


commit d5d488e33d85549c81d007497c8301c920d0a6e4
Merge: ed7642e 0fcdc3d
Author: Daniel Mack <daniel at caiaq.de>
Date:   Tue Nov 24 17:38:25 2009 +0100

    Merge branch 'master' of git://0pointer.de/pulseaudio


commit 1b2cbe92dbffc1d8be12c64fdc647e3cb5e5c58c
Merge: d5d488e 1184666
Author: Daniel Mack <daniel at caiaq.de>
Date:   Tue Dec 1 02:22:39 2009 +0100

    Merge branch 'master' of git://0pointer.de/pulseaudio


commit f9b03d3a44e905d50dfac1483dbf1818d2b84ffa
Merge: 1b2cbe9 e8a5746
Author: Daniel Mack <daniel at caiaq.de>
Date:   Sun Dec 6 15:53:36 2009 +0100

    Merge branch 'master' of git://0pointer.de/pulseaudio


commit 76acaa964e01d119c39ff617691179d54805b412
Author: Kim Lester <kim at dfusion.com.au>
Date:   Wed Sep 16 09:07:50 2009 +0800

    configure.ac: add DARWIN_OS variable
    
    Signed-off-by: Kim Lester <kim at dfusion.com.au>
    Signed-off-by: Daniel Mack <daniel at caiaq.de>

diff --git a/configure.ac b/configure.ac
index af60fff..11af89b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -172,6 +172,10 @@ case $host in
             AC_MSG_RESULT([linux])
             pulse_target_os=linux
         ;;
+        *-*-darwin*)
+            AC_MSG_RESULT([darwin])
+            pulse_target_os=darwin
+        ;;
         *)
             AC_MSG_RESULT([unknown])
             pulse_target_os=unknown
@@ -291,15 +295,21 @@ AC_SUBST([LIBLTDL])
 #### Determine build environment ####
 
 os_is_win32=0
+os_is_darwin=0
 
 case "$host_os" in
         mingw*)
         AC_DEFINE([OS_IS_WIN32], 1, [Build target is Windows.])
         os_is_win32=1
                 ;;
+        darwin*)
+        AC_DEFINE([OS_IS_DARWIN], 1, [Build target is Darwin.])
+        os_is_darwin=1
+                ;;
         esac
 
 AM_CONDITIONAL(OS_IS_WIN32, test "x$os_is_win32" = "x1")
+AM_CONDITIONAL(OS_IS_DARWIN, test "x$os_is_darwin" = "x1")
 
 ###################################
 #   Basic environment checks      #
@@ -403,6 +413,18 @@ AC_SEARCH_LIBS([backtrace], [execinfo])
 # build, disabling its ability to make dlls.
 AC_CHECK_FUNCS([getopt_long], [], [AC_CHECK_LIB([iberty], [getopt_long])])
 
+# Darwin/OS X
+if test "x$os_is_darwin" = "x1" ; then
+    AC_MSG_CHECKING([looking for Apple CoreService Framework])
+    # How do I check a framework "library" - AC_CHECK_LIB prob. won't work??, just assign LIBS & hope
+    AC_CHECK_HEADER([/Developer/Headers/FlatCarbon/CoreServices.h],
+    [LIBS="$LIBS -framework CoreServices"],
+    [AC_MSG_ERROR([CoreServices.h header file not found]) ])
+
+    AC_MSG_RESULT([ok])
+    AC_DEFINE([HAVE_CLOCK_GETTIME], 1, [Using clock_gettime() replacement])
+fi
+
 #### Check for functions ####
 
 # ISO

commit a46ddfebb592852ff791bc90b3953880c49073dd
Author: Daniel Mack <daniel at caiaq.de>
Date:   Sun Nov 1 20:06:08 2009 +0100

    core-rtclock.c: tweak OS_IS_DARWIN constraints
    
    Move the code for OS_IS_DARWIN to the top as on Darwin,
    HAVE_CLOCK_GETTIME is also defined.

diff --git a/src/pulsecore/core-rtclock.c b/src/pulsecore/core-rtclock.c
index 4fe0a47..110158b 100644
--- a/src/pulsecore/core-rtclock.c
+++ b/src/pulsecore/core-rtclock.c
@@ -37,6 +37,7 @@
 #include <CoreServices/CoreServices.h>
 #include <mach/mach.h>
 #include <mach/mach_time.h>
+#include <unistd.h>
 #endif
 
 #include <pulse/timeval.h>
@@ -54,7 +55,19 @@ pa_usec_t pa_rtclock_age(const struct timeval *tv) {
 
 struct timeval *pa_rtclock_get(struct timeval *tv) {
 
-#if defined(HAVE_CLOCK_GETTIME)
+#if defined(OS_IS_DARWIN)
+    uint64_t val, abs_time = mach_absolute_time();
+    Nanoseconds nanos;
+
+    nanos = AbsoluteToNanoseconds(*(AbsoluteTime *) &abs_time);
+    val = *(uint64_t *) &nanos;
+
+    tv->tv_sec = val / PA_NSEC_PER_SEC;
+    tv->tv_usec = (val % PA_NSEC_PER_SEC) / PA_NSEC_PER_USEC;
+
+    return tv;
+
+#elif defined(HAVE_CLOCK_GETTIME)
     struct timespec ts;
 
 #ifdef CLOCK_MONOTONIC
@@ -75,65 +88,39 @@ struct timeval *pa_rtclock_get(struct timeval *tv) {
     tv->tv_usec = ts.tv_nsec / PA_NSEC_PER_USEC;
 
     return tv;
-
-#elif defined(OS_IS_DARWIN)
-    static mach_timebase_info_data_t   tbi;
-    uint64_t nticks;
-    uint64_t time_nsec;
-
-    /* Refer Apple ADC QA1398
-       Also: http://devworld.apple.com/documentation/Darwin/Conceptual/KernelProgramming/services/services.html
-
-       Note: argument is timespec NOT timeval (timespec uses nsec, timeval uses usec)
-    */
-
-    /* try and be a mite efficient - maybe I should keep the N/D as a float !? */
-    if (tbi.denom == 0)
-        mach_timebase_info(&tbi);
-
-    nticks = mach_absolute_time();
-    time_nsec = nticks * tbi.numer / tbi.denom; // see above
-
-    tv->tv_sec = time_nsec / PA_NSEC_PER_SEC;
-    tv->tv_usec = time_nsec / PA_NSEC_PER_USEC;
-
-    return tv;
-
-#else /* OS_IS_DARWIN */
+#endif /* HAVE_CLOCK_GETTIME */
 
     return pa_gettimeofday(tv);
-
-#endif
 }
 
 pa_bool_t pa_rtclock_hrtimer(void) {
 
-#if defined(HAVE_CLOCK_GETTIME)
+#if defined (OS_IS_DARWIN)
+    mach_timebase_info_data_t tbi;
+    uint64_t time_nsec;
+
+    mach_timebase_info(&tbi);
+
+    /* nsec = nticks * (N/D) - we want 1 tick == resolution !? */
+    time_nsec = tbi.numer / tbi.denom;
+    return time_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
+
+#elif defined(HAVE_CLOCK_GETTIME)
     struct timespec ts;
 
 #ifdef CLOCK_MONOTONIC
 
     if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0)
         return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
+
 #endif /* CLOCK_MONOTONIC */
 
     pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0);
     return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
 
-#elif defined (OS_IS_DARWIN)
-    mach_timebase_info_data_t tbi;
-    uint64_t time_nsec;
-
-    mach_timebase_info(&tbi);
+#endif /* HAVE_CLOCK_GETTIME */
 
-    /* nsec = nticks * (N/D) - we want 1 tick == resolution !? */
-    time_nsec = tbi.numer / tbi.denom;
-    return time_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
-
-#else /* OS_IS_DARWIN */
     return FALSE;
-
-#endif
 }
 
 #define TIMER_SLACK_NS (int) ((500 * PA_NSEC_PER_USEC))

commit 962164a3b7f17f8a981862d7913a420636241a41
Author: Kim Lester <kim at dfusion.com.au>
Date:   Wed Sep 16 09:24:27 2009 +0800

    src/Makefile.am: add specific OS_IS_DARWIN files
    
    Signed-off-by: Kim Lester <kim at dfusion.com.au>
    Signed-off-by: Daniel Mack <daniel at caiaq.de>

diff --git a/src/Makefile.am b/src/Makefile.am
index 11826a4..90160d1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -640,12 +640,21 @@ libpulsecommon_ at PA_MAJORMINORMICRO@_la_SOURCES += \
 		pulsecore/mutex-win32.c pulsecore/mutex.h \
 		pulsecore/thread-win32.c pulsecore/thread.h \
 		pulsecore/semaphore-win32.c pulsecore/semaphore.h
-else
+else !OS_IS_WIN32
+if OS_IS_DARWIN
+libpulsecommon_ at PA_MAJORMINORMICRO@_la_SOURCES += \
+		pulsecore/mutex-posix.c pulsecore/mutex.h \
+		pulsecore/thread-posix.c pulsecore/thread.h \
+		pulsecore/semaphore-osx.c pulsecore/semaphore.h
+libpulsecommon_ at PA_MAJORMINORMICRO@_la_CFLAGS += "-I/Developer/Headers/FlatCarbon/"
+#libpulsecommon_ at PA_MAJORMINORMICRO@_la_LDFLAGS += "-framework CoreServices"
+else !OS_IS_DARWIN
 libpulsecommon_ at PA_MAJORMINORMICRO@_la_SOURCES += \
 		pulsecore/mutex-posix.c pulsecore/mutex.h \
 		pulsecore/thread-posix.c pulsecore/thread.h \
 		pulsecore/semaphore-posix.c pulsecore/semaphore.h
-endif
+endif !OS_IS_DARWIN
+endif !OS_IS_WIN32
 
 if HAVE_X11
 libpulsecommon_ at PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/x11prop.c pulsecore/x11prop.h

commit 17d34462eace417075efa2314999a77e41a3849b
Author: Daniel Mack <daniel at caiaq.de>
Date:   Mon Nov 23 00:12:18 2009 +0100

    poll() is totally broken on Mac OS X
    
    Even on 10.5.8, poll() does not do the right thing. Haven't checked on
    newer versions. Hence, wrap all occurences of poll() to pa_poll and
    emulate that call with select() on OSX. This is totally embarassing.

diff --git a/src/modules/module-sine-source.c b/src/modules/module-sine-source.c
index 9826e5f..53f5335 100644
--- a/src/modules/module-sine-source.c
+++ b/src/modules/module-sine-source.c
@@ -32,7 +32,6 @@
 #include <unistd.h>
 #include <limits.h>
 #include <sys/ioctl.h>
-#include <sys/poll.h>
 
 #include <pulse/rtclock.h>
 #include <pulse/timeval.h>
@@ -48,6 +47,7 @@
 #include <pulsecore/thread.h>
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
+#include <pulsecore/poll.h>
 
 #include "module-sine-source-symdef.h"
 
diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c
index ac48ab1..ce534ce 100644
--- a/src/modules/raop/module-raop-sink.c
+++ b/src/modules/raop/module-raop-sink.c
@@ -32,7 +32,6 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <limits.h>
-#include <poll.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
@@ -60,6 +59,7 @@
 #include <pulsecore/thread.h>
 #include <pulsecore/time-smoother.h>
 #include <pulsecore/socket-util.h>
+#include <pulsecore/poll.h>
 
 #include "module-raop-sink-symdef.h"
 #include "rtp.h"
diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
index c4b0237..e3152dd 100644
--- a/src/modules/raop/raop_client.c
+++ b/src/modules/raop/raop_client.c
@@ -51,12 +51,7 @@
 #include <pulsecore/macro.h>
 #include <pulsecore/strbuf.h>
 #include <pulsecore/random.h>
-
-#ifdef HAVE_POLL_H
-#include <poll.h>
-#else
 #include <pulsecore/poll.h>
-#endif
 
 #include "raop_client.h"
 #include "rtsp_client.h"
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index 5961806..6094eb8 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -45,12 +45,7 @@
 #include <pulsecore/macro.h>
 #include <pulsecore/strbuf.h>
 #include <pulsecore/ioline.h>
-
-#ifdef HAVE_POLL_H
-#include <poll.h>
-#else
 #include <pulsecore/poll.h>
-#endif
 
 #include "rtsp_client.h"
 
diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c
index 6cd089e..8f743ec 100644
--- a/src/pulse/mainloop.c
+++ b/src/pulse/mainloop.c
@@ -32,12 +32,6 @@
 #include <fcntl.h>
 #include <errno.h>
 
-#ifdef HAVE_POLL_H
-#include <poll.h>
-#else
-#include <pulsecore/poll.h>
-#endif
-
 #ifndef HAVE_PIPE
 #include <pulsecore/pipe.h>
 #endif
@@ -47,6 +41,7 @@
 #include <pulse/timeval.h>
 #include <pulse/xmalloc.h>
 
+#include <pulsecore/poll.h>
 #include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/llist.h>
@@ -887,7 +882,7 @@ int pa_mainloop_poll(pa_mainloop *m) {
                     m->prepared_timeout == PA_USEC_INVALID ? NULL : pa_timespec_store(&ts, m->prepared_timeout),
                     NULL);
 #else
-            m->poll_func_ret = poll(
+            m->poll_func_ret = pa_poll(
                     m->pollfds, m->n_pollfds,
                     usec_to_timeout(m->prepared_timeout));
 #endif
diff --git a/src/pulsecore/lock-autospawn.c b/src/pulsecore/lock-autospawn.c
index 65e3563..95ca04a 100644
--- a/src/pulsecore/lock-autospawn.c
+++ b/src/pulsecore/lock-autospawn.c
@@ -33,6 +33,7 @@
 #include <pulse/i18n.h>
 #include <pulse/xmalloc.h>
 
+#include <pulsecore/poll.h>
 #include <pulsecore/mutex.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/core-util.h>
@@ -182,7 +183,7 @@ static void wait_for_ping(void) {
     pfd.fd = pipe_fd[0];
     pfd.events = POLLIN;
 
-    if ((k = poll(&pfd, 1, -1)) != 1) {
+    if ((k = pa_poll(&pfd, 1, -1)) != 1) {
         pa_assert(k < 0);
         pa_assert(errno == EINTR);
     } else if ((s = read(pipe_fd[0], &x, 1)) != 1) {
diff --git a/src/pulsecore/poll.c b/src/pulsecore/poll.c
index 46a69c5..1dcace8 100644
--- a/src/pulsecore/poll.c
+++ b/src/pulsecore/poll.c
@@ -43,13 +43,18 @@
 
 #include "winsock.h"
 
-#ifndef HAVE_POLL_H
-
 #include <pulsecore/core-util.h>
+#include <pulse/util.h>
 
 #include "poll.h"
 
-int poll (struct pollfd *fds, unsigned long int nfds, int timeout) {
+/* Mac OSX fails to implement poll() in a working way since 10.4. IOW, for
+ * several years. We need to enable a dirty workaround and emulate that call
+ * with select(), just like for Windows. sic! */
+
+#if !defined(HAVE_POLL_H) || defined(OS_IS_DARWIN)
+
+int pa_poll (struct pollfd *fds, unsigned long int nfds, int timeout) {
     struct timeval tv;
     fd_set rset, wset, xset;
     struct pollfd *f;
diff --git a/src/pulsecore/poll.h b/src/pulsecore/poll.h
index fe0c6af..a137d97 100644
--- a/src/pulsecore/poll.h
+++ b/src/pulsecore/poll.h
@@ -24,6 +24,10 @@
    Copyright (C) 1994,96,97,98,99,2000,2001,2004 Free Software Foundation, Inc.
 ***/
 
+#if defined(HAVE_POLL_H)
+#include <poll.h>
+#else
+
 /* Event types that can be polled for.  These bits may be set in `events'
    to indicate the interesting event types; they will appear in `revents'
    to indicate the status of the file descriptor.  */
@@ -38,10 +42,6 @@
 #define POLLHUP         0x010           /* Hung up.  */
 #define POLLNVAL        0x020           /* Invalid polling request.  */
 
-
-/* Type used for the number of file descriptors.  */
-typedef unsigned long int nfds_t;
-
 /* Data structure describing a polling request.  */
 struct pollfd
   {
@@ -50,9 +50,17 @@ struct pollfd
     short int revents;          /* Types of events that actually occurred.  */
   };
 
+
 /* Poll the file descriptors described by the NFDS structures starting at
    FDS.  If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
    an event to occur; if TIMEOUT is -1, block until an event occurs.
    Returns the number of file descriptors with events, zero if timed out,
    or -1 for errors.  */
-extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);
+
+#endif /* HAVE_POLL_H */
+
+#if defined(HAVE_POLL_H) && !defined(OS_IS_DARWIN)
+#define pa_poll(fds,nfds,timeout) poll((fds),(nfds),(timeout))
+#else
+int pa_poll (struct pollfd *fds, unsigned long nfds, int timeout);
+#endif
diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c
index 666cbc9..83993f0 100644
--- a/src/pulsecore/rtpoll.c
+++ b/src/pulsecore/rtpoll.c
@@ -30,15 +30,10 @@
 #include <string.h>
 #include <errno.h>
 
-#ifdef HAVE_POLL_H
-#include <poll.h>
-#else
-#include <pulsecore/poll.h>
-#endif
-
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
 
+#include <pulsecore/poll.h>
 #include <pulsecore/core-error.h>
 #include <pulsecore/core-rtclock.h>
 #include <pulsecore/macro.h>
@@ -304,7 +299,7 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) {
         r = ppoll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? &ts : NULL, NULL);
     }
 #else
-    r = poll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1);
+    r = pa_poll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1);
 #endif
 
     p->timer_elapsed = r == 0;
diff --git a/src/tests/lock-autospawn-test.c b/src/tests/lock-autospawn-test.c
index c754e23..6f7156d 100644
--- a/src/tests/lock-autospawn-test.c
+++ b/src/tests/lock-autospawn-test.c
@@ -23,9 +23,9 @@
 #include <config.h>
 #endif
 
-#include <sys/poll.h>
 #include <string.h>
 
+#include <pulsecore/poll.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/lock-autospawn.h>
@@ -69,7 +69,7 @@ static void thread_func2(void *k) {
         pollfd.fd = fd;
         pollfd.events = POLLIN;
 
-        pa_assert_se(poll(&pollfd, 1, -1) == 1);
+        pa_assert_se(pa_poll(&pollfd, 1, -1) == 1);
 
         pa_log("%i, woke up", PA_PTR_TO_INT(k));
     }
diff --git a/src/tests/rtpoll-test.c b/src/tests/rtpoll-test.c
index 1706cdf..6a6b73a 100644
--- a/src/tests/rtpoll-test.c
+++ b/src/tests/rtpoll-test.c
@@ -22,8 +22,8 @@
 #endif
 
 #include <signal.h>
-#include <poll.h>
 
+#include <pulsecore/poll.h>
 #include <pulsecore/log.h>
 #include <pulsecore/rtpoll.h>
 
diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c
index ef58e9c..6ffe94f 100644
--- a/src/utils/pacmd.c
+++ b/src/utils/pacmd.c
@@ -25,7 +25,6 @@
 
 #include <assert.h>
 #include <signal.h>
-#include <sys/poll.h>
 #include <sys/socket.h>
 #include <unistd.h>
 #include <errno.h>
@@ -38,6 +37,7 @@
 #include <pulse/xmalloc.h>
 #include <pulse/i18n.h>
 
+#include <pulsecore/poll.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/log.h>
@@ -153,7 +153,7 @@ int main(int argc, char*argv[]) {
         else if (!ibuf_eof)
             pollfd[WATCH_STDIN].events |= POLLIN;
 
-        if (poll(pollfd, N_WATCH, -1) < 0) {
+        if (pa_poll(pollfd, N_WATCH, -1) < 0) {
 
             if (errno == EINTR)
                 continue;

commit 28a73ad1203efc6f6dc33629ce653f45081210fa
Author: Daniel Mack <daniel at caiaq.de>
Date:   Mon Dec 7 00:40:03 2009 +0100

    hack around another OS X bug: recv() with MSG_PEEK does not work
    
    At least for pipes, recv() with MSG_PEEK does actually eat up data from
    file descriptors. Hence, this can't be used for PULLHUP emulation.
    
    Use another ioctl hack for that.

diff --git a/src/pulsecore/poll.c b/src/pulsecore/poll.c
index 1dcace8..b98fb16 100644
--- a/src/pulsecore/poll.c
+++ b/src/pulsecore/poll.c
@@ -35,6 +35,10 @@
 #include <config.h>
 #endif
 
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
 #include <errno.h>
 
 #ifdef HAVE_SYS_SELECT_H
@@ -60,7 +64,9 @@ int pa_poll (struct pollfd *fds, unsigned long int nfds, int timeout) {
     struct pollfd *f;
     int ready;
     int maxfd = 0;
+#ifdef OS_IS_WIN32
     char data[64];
+#endif
 
     FD_ZERO (&rset);
     FD_ZERO (&wset);
@@ -103,6 +109,7 @@ int pa_poll (struct pollfd *fds, unsigned long int nfds, int timeout) {
     ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset,
                     SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset,
                     SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv));
+
     if ((ready == -1) && (errno == EBADF)) {
         ready = 0;
 
@@ -165,6 +172,8 @@ int pa_poll (struct pollfd *fds, unsigned long int nfds, int timeout) {
 #endif
 
     if (ready > 0) {
+        int r;
+
         ready = 0;
         for (f = fds; f < &fds[nfds]; ++f) {
             f->revents = 0;
@@ -172,6 +181,18 @@ int pa_poll (struct pollfd *fds, unsigned long int nfds, int timeout) {
                 if (FD_ISSET (f->fd, &rset)) {
                     /* support for POLLHUP.  An hung up descriptor does not
                        increase the return value! */
+#ifdef OS_IS_DARWIN
+                    /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK
+                     * for some kinds of descriptors.  Detect if this descriptor is a
+                     * connected socket, a server socket, or something else using a
+                     * 0-byte recv, and use ioctl(2) to detect POLLHUP.  */
+                    r = recv(f->fd, NULL, 0, MSG_PEEK);
+		    if (r == 0 || (r < 0 && errno == ENOTSOCK))
+		        ioctl(f->fd, FIONREAD, &r);
+
+		    if (r == 0)
+		        f->revents |= POLLHUP;
+#else /* !OS_IS_DARWIN */
                     if (recv (f->fd, data, 64, MSG_PEEK) == -1) {
                         if (errno == ESHUTDOWN || errno == ECONNRESET ||
                             errno == ECONNABORTED || errno == ENETRESET) {
@@ -179,6 +200,7 @@ int pa_poll (struct pollfd *fds, unsigned long int nfds, int timeout) {
                             f->revents |= POLLHUP;
                         }
                     }
+#endif
 
                     if (f->revents == 0)
                         f->revents |= POLLIN;

commit a23f5cf33d21461bdb6e7bb19dd3426041ca5bc1
Author: Daniel Mack <daniel at caiaq.de>
Date:   Tue Sep 22 11:10:26 2009 +0800

    CoreAudio: add device detection module
    
    This adds a new module for CoreAudio device detection. It registers a
    callback to detect hotplugged devices and creates/destroys modules named
    'module-coreaudio-device'. Devices are identified via a system-wide
    unique AudioDeviceID.

diff --git a/src/Makefile.am b/src/Makefile.am
index 90160d1..70ab5b0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1066,6 +1066,11 @@ modlibexec_LTLIBRARIES += \
 		module-oss.la
 endif
 
+if HAVE_COREAUDIO
+modlibexec_LTLIBRARIES += \
+		module-coreaudio-detect.la
+endif
+
 pulselibexec_PROGRAMS =
 
 if HAVE_ALSA
@@ -1238,6 +1243,7 @@ SYMDEF_FILES = \
 		modules/alsa/module-alsa-sink-symdef.h \
 		modules/alsa/module-alsa-source-symdef.h \
 		modules/alsa/module-alsa-card-symdef.h \
+		modules/coreaudio/module-coreaudio-detect-symdef.h \
 		modules/module-solaris-symdef.h \
 		modules/module-waveout-symdef.h \
 		modules/module-detect-symdef.h \
@@ -1469,6 +1475,14 @@ module_oss_la_SOURCES = modules/oss/module-oss.c
 module_oss_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_oss_la_LIBADD = $(AM_LIBADD) liboss-util.la libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 
+# COREAUDIO
+
+module_coreaudio_detect_la_SOURCES = modules/coreaudio/module-coreaudio-detect.c
+module_coreaudio_detect_la_LDFLAGS = $(MODULE_LDFLAGS) \
+			-Wl,-framework -Wl,Cocoa -framework CoreAudio \
+			-Wl,-framework -Wl,AudioUnit -framework AudioUnit
+module_coreaudio_detect_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+
 # ALSA
 
 libalsa_util_la_SOURCES = modules/alsa/alsa-util.c modules/alsa/alsa-util.h modules/alsa/alsa-mixer.c modules/alsa/alsa-mixer.h modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h modules/alsa/alsa-source.c modules/alsa/alsa-source.h modules/reserve-wrap.c modules/reserve-wrap.h
diff --git a/src/modules/coreaudio/module-coreaudio-detect.c b/src/modules/coreaudio/module-coreaudio-detect.c
new file mode 100644
index 0000000..872678e
--- /dev/null
+++ b/src/modules/coreaudio/module-coreaudio-detect.c
@@ -0,0 +1,229 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Daniel Mack <daniel at caiaq.de>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/llist.h>
+
+#include <CoreAudio/CoreAudio.h>
+
+#include "module-coreaudio-detect-symdef.h"
+
+#define DEVICE_MODULE_NAME "module-coreaudio-device"
+
+PA_MODULE_AUTHOR("Daniel Mack");
+PA_MODULE_DESCRIPTION("CoreAudio device detection");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("");
+
+typedef struct ca_device ca_device;
+
+struct ca_device {
+    AudioDeviceID id;
+    unsigned int  module_index;
+    PA_LLIST_FIELDS(ca_device);
+};
+
+struct userdata {
+    int detect_fds[2];
+    pa_io_event *detect_io;
+
+    PA_LLIST_HEAD(ca_device, devices);
+};
+
+static int ca_device_added(struct pa_module *m, AudioDeviceID id) {
+    pa_module *mod;
+    struct userdata *u = m->userdata;
+    struct ca_device *dev;
+    char *args;
+
+    pa_assert(u);
+
+    args = pa_sprintf_malloc("device_id=%d", (int) id);
+    pa_log_debug("Loading %s with arguments '%s'", DEVICE_MODULE_NAME, args);
+    mod = pa_module_load(m->core, DEVICE_MODULE_NAME, args);
+    pa_xfree(args);
+
+    if (!mod) {
+        pa_log_info("Failed to load module %s with arguments '%s'", DEVICE_MODULE_NAME, args);
+        return -1;
+    }
+
+    dev = pa_xnew0(ca_device, 1);
+    dev->module_index = mod->index;
+    dev->id = id;
+
+    PA_LLIST_INIT(ca_device, dev);
+    PA_LLIST_PREPEND(ca_device, u->devices, dev);
+
+    return 0;
+}
+
+static int ca_update_device_list(struct pa_module *m) {
+    OSStatus err;
+    UInt32 i, size, num_devices;
+    Boolean writable;
+    AudioDeviceID *device_id;
+    struct ca_device *dev;
+    struct userdata *u = m->userdata;
+
+    pa_assert(u);
+
+    /* get the number of currently available audio devices */
+    err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &writable);
+    if (err) {
+        pa_log("Unable to get info for kAudioHardwarePropertyDevices.");
+        return -1;
+    }
+
+    num_devices = size / sizeof(AudioDeviceID);
+    device_id = pa_xnew(AudioDeviceID, num_devices);
+
+    err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, device_id);
+    if (err) {
+        pa_log("Unable to get kAudioHardwarePropertyDevices.");
+        pa_xfree(device_id);
+        return -1;
+    }
+
+    /* scan for devices which are reported but not in our cached list */
+    for (i = 0; i < num_devices; i++) {
+        bool found = FALSE;
+
+        PA_LLIST_FOREACH(dev, u->devices)
+            if (dev->id == device_id[i]) {
+                found = TRUE;
+                break;
+            }
+
+        if (!found)
+            ca_device_added(m, device_id[i]);
+    }
+
+    /* scan for devices which are in our cached list but are not reported */
+scan_removed:
+
+    PA_LLIST_FOREACH(dev, u->devices) {
+        bool found = FALSE;
+
+        for (i = 0; i < num_devices; i++)
+            if (dev->id == device_id[i]) {
+                found = TRUE;
+                break;
+            }
+
+        if (!found) {
+            pa_log_debug("device id %d has been removed (module index %d)  %p", (unsigned int) dev->id, dev->module_index, dev);
+            pa_module_unload_request_by_index(m->core, dev->module_index, TRUE);
+            PA_LLIST_REMOVE(ca_device, u->devices, dev);
+            pa_xfree(dev);
+            /* the current list item pointer is not valid anymore, so start over. */
+            goto scan_removed;
+        }
+    }
+
+    pa_xfree(device_id);
+    return 0;
+}
+
+static OSStatus property_listener_proc(AudioHardwarePropertyID property, void *data) {
+    struct userdata *u = data;
+    char dummy = 1;
+
+    pa_assert(u);
+
+    /* dispatch module load/unload operations in main thread */
+    if (property == kAudioHardwarePropertyDevices)
+        write(u->detect_fds[1], &dummy, 1);
+
+    return 0;
+}
+
+static void detect_handle(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+    pa_module *m = userdata;
+    char dummy;
+
+    pa_assert(m);
+
+    read(fd, &dummy, 1);
+    ca_update_device_list(m);
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u = pa_xnew0(struct userdata, 1);
+
+    m->userdata = u;
+
+    if (AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices, property_listener_proc, u)) {
+        pa_log("AudioHardwareAddPropertyListener() failed.");
+        goto fail;
+    }
+
+    if (ca_update_device_list(m))
+       goto fail;
+
+    pa_assert_se(pipe(u->detect_fds) == 0);
+    pa_assert_se(u->detect_io = m->core->mainloop->io_new(m->core->mainloop, u->detect_fds[0], PA_IO_EVENT_INPUT, detect_handle, m));
+
+    return 0;
+
+fail:
+    pa_xfree(u);
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u = m->userdata;
+    struct ca_device *dev = u->devices;
+
+    pa_assert(u);
+
+    AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, property_listener_proc);
+
+    while (dev) {
+        struct ca_device *next = dev->next;
+
+        pa_module_unload_request_by_index(m->core, dev->module_index, TRUE);
+        pa_xfree(dev);
+
+        dev = next;
+    }
+
+    if (u->detect_fds[0] >= 0)
+        close(u->detect_fds[0]);
+
+    if (u->detect_fds[1] >= 0)
+        close(u->detect_fds[1]);
+
+    if (u->detect_io)
+        m->core->mainloop->io_free(u->detect_io);
+
+    pa_xfree(u);
+}

commit 7732421a27870a832d7121bc8342503f2fbf3c2a
Author: Daniel Mack <daniel at caiaq.de>
Date:   Tue Sep 22 11:27:57 2009 +0800

    CoreAudio: add audio device module
    
    This patch adds support for CoreAudio driven devices under Mac OS X. It
    is typically instanciated by the CoreAudio device detection module and
    handles all available streams on a specific device.
    
    Sinks are created according to the reported stream configuration.
    Float32 is used as default audio sample format at it is the only format
    CoreAudio speaks natively.
    
    Hardware volume control is not implemented yet.

diff --git a/src/Makefile.am b/src/Makefile.am
index 70ab5b0..fa5d170 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1068,7 +1068,8 @@ endif
 
 if HAVE_COREAUDIO
 modlibexec_LTLIBRARIES += \
-		module-coreaudio-detect.la
+		module-coreaudio-detect.la \
+		module-coreaudio-device.la
 endif
 
 pulselibexec_PROGRAMS =
@@ -1244,6 +1245,7 @@ SYMDEF_FILES = \
 		modules/alsa/module-alsa-source-symdef.h \
 		modules/alsa/module-alsa-card-symdef.h \
 		modules/coreaudio/module-coreaudio-detect-symdef.h \
+		modules/coreaudio/module-coreaudio-device-symdef.h \
 		modules/module-solaris-symdef.h \
 		modules/module-waveout-symdef.h \
 		modules/module-detect-symdef.h \
@@ -1483,6 +1485,11 @@ module_coreaudio_detect_la_LDFLAGS = $(MODULE_LDFLAGS) \
 			-Wl,-framework -Wl,AudioUnit -framework AudioUnit
 module_coreaudio_detect_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 
+module_coreaudio_device_la_SOURCES = modules/coreaudio/module-coreaudio-device.c
+module_coreaudio_device_la_LDFLAGS = $(MODULE_LDFLAGS) \
+			-Wl,-framework -Wl,Cocoa -framework CoreAudio \
+			-Wl,-framework -Wl,AudioUnit -framework AudioUnit
+module_coreaudio_device_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 # ALSA
 
 libalsa_util_la_SOURCES = modules/alsa/alsa-util.c modules/alsa/alsa-util.h modules/alsa/alsa-mixer.c modules/alsa/alsa-mixer.h modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h modules/alsa/alsa-source.c modules/alsa/alsa-source.h modules/reserve-wrap.c modules/reserve-wrap.h
diff --git a/src/modules/coreaudio/module-coreaudio-device.c b/src/modules/coreaudio/module-coreaudio-device.c
new file mode 100644
index 0000000..a3699e6
--- /dev/null
+++ b/src/modules/coreaudio/module-coreaudio-device.c
@@ -0,0 +1,820 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Daniel Mack <daniel at caiaq.de>
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* TODO:
+    - implement hardware volume controls
+    - handle audio device stream format changes (will require changes to the core)
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/card.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+
+#include <CoreAudio/CoreAudio.h>
+#include <CoreAudio/CoreAudioTypes.h>
+#include <CoreAudio/AudioHardware.h>
+
+#include "module-coreaudio-device-symdef.h"
+
+#define DEFAULT_FRAMES_PER_IOPROC 512
+
+PA_MODULE_AUTHOR("Daniel Mack");
+PA_MODULE_DESCRIPTION("CoreAudio device");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE("device_id=<the CoreAudio device id> "
+                "ioproc_frames=<audio frames per IOProc call> ");
+
+static const char* const valid_modargs[] = {
+    "device_id",
+    "ioproc_frames",
+    NULL
+};
+
+enum {
+    CA_MESSAGE_RENDER = PA_SINK_MESSAGE_MAX,
+};
+
+typedef struct coreaudio_sink coreaudio_sink;
+typedef struct coreaudio_source coreaudio_source;
+
+struct userdata {
+    AudioDeviceID device_id;
+    AudioDeviceIOProcID proc_id;
+
+    pa_thread_mq thread_mq;
+    pa_asyncmsgq *async_msgq;
+
+    pa_rtpoll *rtpoll;
+    pa_thread *thread;
+
+    pa_module *module;
+    pa_card *card;
+    pa_bool_t running;
+
+    char *device_name, *vendor_name;
+
+    const AudioBufferList *render_input_data;
+    AudioBufferList       *render_output_data;
+
+    AudioStreamBasicDescription stream_description;
+
+    PA_LLIST_HEAD(coreaudio_sink, sinks);
+    PA_LLIST_HEAD(coreaudio_source, sources);
+};
+
+struct coreaudio_sink {
+    pa_sink *pa_sink;
+    struct userdata *userdata;
+
+    char *name;
+    unsigned int channel_idx;
+    pa_bool_t active;
+
+    pa_channel_map map;
+    pa_sample_spec ss;
+
+    PA_LLIST_FIELDS(coreaudio_sink);
+};
+
+struct coreaudio_source {
+    pa_source *pa_source;
+    struct userdata *userdata;
+
+    char *name;
+    unsigned int channel_idx;
+    pa_bool_t active;
+
+    pa_channel_map map;
+    pa_sample_spec ss;
+
+    PA_LLIST_FIELDS(coreaudio_source);
+};
+
+static OSStatus io_render_proc (AudioDeviceID          device,
+                                const AudioTimeStamp  *now,
+                                const AudioBufferList *inputData,
+                                const AudioTimeStamp  *inputTime,
+                                AudioBufferList       *outputData,
+                                const AudioTimeStamp  *outputTime,
+                                void                  *clientData)
+{
+    struct userdata *u = clientData;
+
+    pa_assert(u);
+    pa_assert(device == u->device_id);
+
+    u->render_input_data = inputData;
+    u->render_output_data = outputData;
+
+    if (u->sinks)
+        pa_assert_se(pa_asyncmsgq_send(u->async_msgq, PA_MSGOBJECT(u->sinks->pa_sink),
+                                        CA_MESSAGE_RENDER, NULL, 0, NULL) == 0);
+
+    if (u->sources)
+        pa_assert_se(pa_asyncmsgq_send(u->async_msgq, PA_MSGOBJECT(u->sources->pa_source),
+                                        CA_MESSAGE_RENDER, NULL, 0, NULL) == 0);
+
+    return 0;
+}
+
+static OSStatus ca_stream_format_changed(AudioDeviceID inDevice,
+                                         UInt32 inChannel,
+                                         Boolean isInput,
+                                         AudioDevicePropertyID inPropertyID,
+                                         void *inClientData)
+{
+    struct userdata *u = inClientData;
+
+    pa_assert(u);
+
+    /* REVISIT: PA can't currently handle external format change requests.
+     * Hence, we set the original format back in this callback to avoid horrible audio artefacts.
+     * The device settings will appear to be 'locked' for any application as long as the PA daemon is running.
+     * Once we're able to propagate such events up in the core, this needs to be changed. */
+
+    return AudioDeviceSetProperty(inDevice, NULL, inChannel, isInput,
+                                  kAudioDevicePropertyStreamFormat, sizeof(u->stream_description), &u->stream_description);
+}
+
+static pa_usec_t get_latency_us(pa_object *o) {
+    struct userdata *u;
+    pa_sample_spec *ss;
+    bool is_source;
+    UInt32 v, total = 0;
+    UInt32 err, size = sizeof(v);
+
+    if (pa_sink_isinstance(o)) {
+        coreaudio_sink *sink = PA_SINK(o)->userdata;
+
+        u = sink->userdata;
+        ss = &sink->ss;
+        is_source = FALSE;
+    } else if (pa_source_isinstance(o)) {
+        coreaudio_source *source = PA_SOURCE(o)->userdata;
+
+        u = source->userdata;
+        ss = &source->ss;
+        is_source = TRUE;
+    } else
+        pa_assert_not_reached();
+
+    pa_assert(u);
+
+    /* get the device latency */
+    size = sizeof(total);
+    AudioDeviceGetProperty(u->device_id, 0, is_source, kAudioDevicePropertyLatency, &size, &v);
+    total += v;
+
+    /* the the IOProc buffer size */
+    size = sizeof(v);
+    AudioDeviceGetProperty(u->device_id, 0, is_source, kAudioDevicePropertyBufferFrameSize, &size, &v);
+    total += v;
+
+    /* IOProc safety offset - this value is the same for both directions, hence we divide it by 2 */
+    size = sizeof(v);
+    AudioDeviceGetProperty(u->device_id, 0, is_source, kAudioDevicePropertySafetyOffset, &size, &v);
+    total += v / 2;
+
+    /* get the stream latency.
+     * FIXME: this assumes the stream latency is the same for all streams */
+    err = AudioStreamGetProperty(0, is_source, kAudioStreamPropertyLatency, &size, &v);
+    if (!err)
+        total += v;
+
+    return pa_bytes_to_usec(total * pa_frame_size(ss), ss);
+}
+
+static void ca_device_check_device_state(struct userdata *u) {
+    coreaudio_sink *ca_sink;
+    coreaudio_source *ca_source;
+    pa_bool_t active = FALSE;
+
+    pa_assert(u);
+
+    for (ca_sink = u->sinks; ca_sink; ca_sink = ca_sink->next)
+        if (ca_sink->active)
+            active = TRUE;
+
+    for (ca_source = u->sources; ca_source; ca_source = ca_source->next)
+        if (ca_source->active)
+            active = TRUE;
+
+    if (active && !u->running)
+        AudioDeviceStart(u->device_id, u->proc_id);
+    else if (!active && u->running)
+        AudioDeviceStop(u->device_id, u->proc_id);
+
+    u->running = active;
+}
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    coreaudio_sink *sink = PA_SINK(o)->userdata;
+    struct userdata *u = sink->userdata;
+    unsigned int i;
+    pa_memchunk audio_chunk;
+
+    switch (code) {
+        case CA_MESSAGE_RENDER: {
+            /* audio out */
+            for (i = 0; i < u->render_output_data->mNumberBuffers; i++) {
+                AudioBuffer *buf = u->render_output_data->mBuffers + i;
+
+                pa_assert(sink);
+
+                if (PA_SINK_IS_OPENED(sink->pa_sink->thread_info.state)) {
+                    if (sink->pa_sink->thread_info.rewind_requested)
+                        pa_sink_process_rewind(sink->pa_sink, 0);
+
+                    audio_chunk.memblock = pa_memblock_new_fixed(u->module->core->mempool, buf->mData, buf->mDataByteSize, FALSE);
+                    audio_chunk.length = buf->mDataByteSize;
+                    audio_chunk.index = 0;
+
+                    pa_sink_render_into_full(sink->pa_sink, &audio_chunk);
+                    pa_memblock_unref_fixed(audio_chunk.memblock);
+                }
+
+                sink = sink->next;
+            }
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            *((pa_usec_t *) data) = get_latency_us(PA_OBJECT(o));
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_SET_STATE:
+            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+                case PA_SINK_SUSPENDED:
+                case PA_SINK_IDLE:
+                    sink->active = FALSE;
+                    break;
+
+                case PA_SINK_RUNNING:
+                    sink->active = TRUE;
+                    break;
+
+                case PA_SINK_UNLINKED:
+                case PA_SINK_INIT:
+                case PA_SINK_INVALID_STATE:
+                    ;
+            }
+
+            ca_device_check_device_state(sink->userdata);
+            break;
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    coreaudio_source *source = PA_SOURCE(o)->userdata;
+    struct userdata *u = source->userdata;
+    unsigned int i;
+    pa_memchunk audio_chunk;
+
+    switch (code) {
+        case CA_MESSAGE_RENDER: {
+            /* audio in */
+            for (i = 0; i < u->render_input_data->mNumberBuffers; i++) {
+                const AudioBuffer *buf = u->render_input_data->mBuffers + i;
+
+                pa_assert(source);
+
+                if (PA_SOURCE_IS_OPENED(source->pa_source->thread_info.state)) {
+                    audio_chunk.memblock = pa_memblock_new_fixed(u->module->core->mempool, buf->mData, buf->mDataByteSize, TRUE);
+                    audio_chunk.length = buf->mDataByteSize;
+                    audio_chunk.index = 0;
+
+                    pa_source_post(source->pa_source, &audio_chunk);
+                    pa_memblock_unref_fixed(audio_chunk.memblock);
+                }
+
+                source = source->next;
+            }
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            *((pa_usec_t *) data) = get_latency_us(PA_OBJECT(o));
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_SET_STATE:
+            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
+                case PA_SOURCE_SUSPENDED:
+                case PA_SOURCE_IDLE:
+                    source->active = FALSE;
+                    break;
+
+                case PA_SOURCE_RUNNING:
+                    source->active = TRUE;
+                    break;
+
+                case PA_SOURCE_UNLINKED:
+                case PA_SOURCE_INIT:
+                case PA_SOURCE_INVALID_STATE:
+                    ;
+            }
+
+            ca_device_check_device_state(source->userdata);
+            break;
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+static int ca_device_create_sink(pa_module *m, AudioBuffer *buf, int channel_idx) {
+    OSStatus err;
+    UInt32 size;
+    struct userdata *u = m->userdata;
+    pa_sink_new_data new_data;
+    pa_sink_flags_t flags = PA_SINK_LATENCY | PA_SINK_HARDWARE;
+    coreaudio_sink *ca_sink;
+    pa_sink *sink;
+    unsigned int i;
+    char tmp[255];
+    pa_strbuf *strbuf;
+
+    ca_sink = pa_xnew0(coreaudio_sink, 1);
+    ca_sink->map.channels = buf->mNumberChannels;
+    ca_sink->ss.channels = buf->mNumberChannels;
+    ca_sink->channel_idx = channel_idx;
+
+    /* build a name for this stream */
+    strbuf = pa_strbuf_new();
+
+    for (i = 0; i < buf->mNumberChannels; i++) {
+        size = sizeof(tmp);
+        err = AudioDeviceGetProperty(u->device_id, channel_idx + i + 1, 0, kAudioObjectPropertyElementName, &size, tmp);
+        if (err || !strlen(tmp))
+            snprintf(tmp, sizeof(tmp), "Channel %d", channel_idx + i + 1);
+
+        if (i > 0)
+            pa_strbuf_puts(strbuf, ", ");
+
+        pa_strbuf_puts(strbuf, tmp);
+    }
+
+    ca_sink->name = pa_strbuf_tostring_free(strbuf);
+
+    pa_log_debug("Stream name is >%s<", ca_sink->name);
+
+    /* default to mono streams */
+    for (i = 0; i < ca_sink->map.channels; i++)
+        ca_sink->map.map[i] = PA_CHANNEL_POSITION_MONO;
+
+    if (buf->mNumberChannels == 2) {
+        ca_sink->map.map[0] = PA_CHANNEL_POSITION_LEFT;
+        ca_sink->map.map[1] = PA_CHANNEL_POSITION_RIGHT;
+    }
+
+    ca_sink->ss.rate = u->stream_description.mSampleRate;
+    ca_sink->ss.format = PA_SAMPLE_FLOAT32LE;
+
+    pa_sink_new_data_init(&new_data);
+    new_data.card = u->card;
+    new_data.driver = __FILE__;
+    new_data.module = u->module;
+    new_data.namereg_fail = FALSE;
+    pa_sink_new_data_set_name(&new_data, ca_sink->name);
+    pa_sink_new_data_set_channel_map(&new_data, &ca_sink->map);
+    pa_sink_new_data_set_sample_spec(&new_data, &ca_sink->ss);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_PRODUCT_NAME, u->device_name);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, u->device_name);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "mmap");
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_CLASS, "sound");
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_API, "CoreAudio");
+    pa_proplist_setf(new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) buf->mDataByteSize);
+
+    if (u->vendor_name)
+        pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_VENDOR_NAME, u->vendor_name);
+
+    sink = pa_sink_new(m->core, &new_data, flags);
+    pa_sink_new_data_done(&new_data);
+
+    if (!sink) {
+        pa_log("unable to create sink.");
+        return -1;
+    }
+
+    sink->parent.process_msg = sink_process_msg;
+    sink->userdata = ca_sink;
+
+    pa_sink_set_asyncmsgq(sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(sink, u->rtpoll);
+
+    ca_sink->pa_sink = sink;
+    ca_sink->userdata = u;
+
+    PA_LLIST_PREPEND(coreaudio_sink, u->sinks, ca_sink);
+
+    return 0;
+}
+
+static int ca_device_create_source(pa_module *m, AudioBuffer *buf, int channel_idx) {
+    OSStatus err;
+    UInt32 size;
+    struct userdata *u = m->userdata;
+    pa_source_new_data new_data;
+    pa_source_flags_t flags = PA_SOURCE_LATENCY | PA_SOURCE_HARDWARE;
+    coreaudio_source *ca_source;
+    pa_source *source;
+    unsigned int i;
+    char tmp[255];
+    pa_strbuf *strbuf;
+
+    ca_source = pa_xnew0(coreaudio_source, 1);
+    ca_source->map.channels = buf->mNumberChannels;
+    ca_source->ss.channels = buf->mNumberChannels;
+    ca_source->channel_idx = channel_idx;
+
+    /* build a name for this stream */
+    strbuf = pa_strbuf_new();
+
+    for (i = 0; i < buf->mNumberChannels; i++) {
+        size = sizeof(tmp);
+        err = AudioDeviceGetProperty(u->device_id, channel_idx + i + 1, 0, kAudioObjectPropertyElementName, &size, tmp);
+        if (err || !strlen(tmp))
+            snprintf(tmp, sizeof(tmp), "Channel %d", channel_idx + i + 1);
+
+        if (i > 0)
+            pa_strbuf_puts(strbuf, ", ");
+
+        pa_strbuf_puts(strbuf, tmp);
+    }
+
+    ca_source->name = pa_strbuf_tostring_free(strbuf);
+
+    pa_log_debug("Stream name is >%s<", ca_source->name);
+
+    /* default to mono streams */
+    for (i = 0; i < ca_source->map.channels; i++)
+        ca_source->map.map[i] = PA_CHANNEL_POSITION_MONO;
+
+    if (buf->mNumberChannels == 2) {
+        ca_source->map.map[0] = PA_CHANNEL_POSITION_LEFT;
+        ca_source->map.map[1] = PA_CHANNEL_POSITION_RIGHT;
+    }
+
+    ca_source->ss.rate = u->stream_description.mSampleRate;
+    ca_source->ss.format = PA_SAMPLE_FLOAT32LE;
+
+    pa_source_new_data_init(&new_data);
+    new_data.card = u->card;
+    new_data.driver = __FILE__;
+    new_data.module = u->module;
+    new_data.namereg_fail = FALSE;
+    pa_source_new_data_set_name(&new_data, ca_source->name);
+    pa_source_new_data_set_channel_map(&new_data, &ca_source->map);
+    pa_source_new_data_set_sample_spec(&new_data, &ca_source->ss);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_PRODUCT_NAME, u->device_name);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, u->device_name);
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "mmap");
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_CLASS, "sound");
+    pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_API, "CoreAudio");
+    pa_proplist_setf(new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) buf->mDataByteSize);
+
+    if (u->vendor_name)
+        pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_VENDOR_NAME, u->vendor_name);
+
+    source = pa_source_new(m->core, &new_data, flags);
+    pa_source_new_data_done(&new_data);
+
+    if (!source) {
+        pa_log("unable to create source.");
+        return -1;
+    }
+
+    source->parent.process_msg = source_process_msg;
+    source->userdata = ca_source;
+
+    pa_source_set_asyncmsgq(source, u->thread_mq.inq);
+    pa_source_set_rtpoll(source, u->rtpoll);
+
+    ca_source->pa_source = source;
+    ca_source->userdata = u;
+
+    PA_LLIST_PREPEND(coreaudio_source, u->sources, ca_source);
+
+    return 0;
+}
+
+static int ca_device_create_streams(pa_module *m, bool direction_in) {
+    OSStatus err;
+    UInt32 size, i, channel_idx;
+    struct userdata *u = m->userdata;
+    int section = direction_in ? 1 : 0;
+    AudioBufferList *buffer_list;
+
+    /* get current stream format */
+    size = sizeof(AudioStreamBasicDescription);
+    err = AudioDeviceGetProperty(u->device_id, 0, section, kAudioDevicePropertyStreamFormat, &size, &u->stream_description);
+    if (err) {
+        /* no appropriate streams found - silently bail. */
+        return -1;
+    }
+
+    if (u->stream_description.mFormatID != kAudioFormatLinearPCM) {
+        pa_log("Unsupported audio format '%c%c%c%c'",
+            (char) (u->stream_description.mFormatID >> 24),
+            (char) (u->stream_description.mFormatID >> 16) & 0xff,
+            (char) (u->stream_description.mFormatID >> 8) & 0xff,
+            (char) (u->stream_description.mFormatID & 0xff));
+        return -1;
+    }
+
+    /* get stream configuration */
+    size = 0;
+    err = AudioDeviceGetPropertyInfo(u->device_id, 0, section, kAudioDevicePropertyStreamConfiguration, &size, NULL);
+    if (err) {
+        pa_log("Failed to get kAudioDevicePropertyStreamConfiguration (%s).", direction_in ? "input" : "output");
+        return -1;
+    }
+
+    if (!size)
+        return 0;
+
+    buffer_list = (AudioBufferList *) pa_xmalloc(size);
+    err = AudioDeviceGetProperty(u->device_id, 0, section, kAudioDevicePropertyStreamConfiguration, &size, buffer_list);
+
+    if (!err) {
+        pa_log_debug("Sample rate: %f", u->stream_description.mSampleRate);
+        pa_log_debug("%d bytes per packet",   (unsigned int) u->stream_description.mBytesPerPacket);
+        pa_log_debug("%d frames per packet",  (unsigned int) u->stream_description.mFramesPerPacket);
+        pa_log_debug("%d bytes per frame",    (unsigned int) u->stream_description.mBytesPerFrame);
+        pa_log_debug("%d channels per frame", (unsigned int) u->stream_description.mChannelsPerFrame);
+        pa_log_debug("%d bits per channel",   (unsigned int) u->stream_description.mBitsPerChannel);
+
+        for (channel_idx = 0, i = 0; i < buffer_list->mNumberBuffers; i++) {
+            AudioBuffer *buf = buffer_list->mBuffers + i;
+
+            if (direction_in)
+                ca_device_create_source(m, buf, channel_idx);
+            else
+                ca_device_create_sink(m, buf, channel_idx);
+
+            channel_idx += buf->mNumberChannels;
+        }
+    }
+
+    pa_xfree(buffer_list);
+    return 0;
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+    pa_assert(u->module);
+    pa_assert(u->module->core);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->module->core->realtime_scheduling)
+        pa_make_realtime(u->module->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    for (;;) {
+        int ret;
+
+        ret = pa_rtpoll_run(u->rtpoll, TRUE);
+
+        if (ret < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+    }
+
+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->module->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) {
+    OSStatus err;
+    UInt32 size, frames;
+    struct userdata *u = NULL;
+    pa_modargs *ma = NULL;
+    char tmp[64];
+    pa_card_new_data card_new_data;
+    coreaudio_sink *ca_sink;
+    coreaudio_source *ca_source;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    m->userdata = u;
+
+    if (pa_modargs_get_value_u32(ma, "device_id", (unsigned int *) &u->device_id) != 0) {
+        pa_log("Failed to parse device_id argument.");
+        goto fail;
+    }
+
+    /* get device product name */
+    size = sizeof(tmp);
+    err = AudioDeviceGetProperty(u->device_id, 0, 0, kAudioDevicePropertyDeviceName, &size, &tmp);
+    if (err) {
+        pa_log("Failed to get kAudioDevicePropertyDeviceName (err = %08x).", (int) err);
+        goto fail;
+    }
+
+    u->device_name = pa_xstrdup(tmp);
+
+    pa_card_new_data_init(&card_new_data);
+    pa_proplist_sets(card_new_data.proplist, PA_PROP_DEVICE_STRING, tmp);
+    card_new_data.driver = __FILE__;
+    pa_card_new_data_set_name(&card_new_data, tmp);
+    pa_log_info("Initializing module for CoreAudio device '%s' (id %d)", tmp, (unsigned int) u->device_id);
+
+    /* get device vendor name (may fail) */
+    size = sizeof(tmp);
+    err = AudioDeviceGetProperty(u->device_id, 0, 0, kAudioDevicePropertyDeviceManufacturer, &size, &tmp);
+    if (!err)
+        u->vendor_name = pa_xstrdup(tmp);
+
+    /* create the card object */
+    u->card = pa_card_new(m->core, &card_new_data);
+    if (!u->card) {
+        pa_log("Unable to create card.\n");
+        goto fail;
+    }
+
+    pa_card_new_data_done(&card_new_data);
+    u->card->userdata = u;
+
+    u->rtpoll = pa_rtpoll_new();
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+    u->async_msgq = pa_asyncmsgq_new(0);
+    pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->async_msgq);
+
+    PA_LLIST_HEAD_INIT(coreaudio_sink, u->sinks);
+
+    /* create sinks */
+    ca_device_create_streams(m, FALSE);
+
+    /* create sources */
+    ca_device_create_streams(m, TRUE);
+
+    /* create the message thread */
+    if (!(u->thread = pa_thread_new(thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    /* register notification callback for stream format changes */
+    AudioDeviceAddPropertyListener(u->device_id, 0, 0, kAudioDevicePropertyStreamFormat, ca_stream_format_changed, u);
+
+    /* set number of frames in IOProc */
+    frames = DEFAULT_FRAMES_PER_IOPROC;
+    pa_modargs_get_value_u32(ma, "ioproc_frames", (unsigned int *) &frames);
+
+    AudioDeviceSetProperty(u->device_id, NULL, 0, 0, kAudioDevicePropertyBufferFrameSize, sizeof(frames), &frames);
+    pa_log_debug("%u frames per IOProc\n", (unsigned int) frames);
+
+    /* create one ioproc for both directions */
+    err = AudioDeviceCreateIOProcID(u->device_id, io_render_proc, u, &u->proc_id);
+    if (err) {
+        pa_log("AudioDeviceCreateIOProcID() failed (err = %08x\n).", (int) err);
+        goto fail;
+    }
+
+    for (ca_sink = u->sinks; ca_sink; ca_sink = ca_sink->next)
+        pa_sink_put(ca_sink->pa_sink);
+
+    for (ca_source = u->sources; ca_source; ca_source = ca_source->next)
+        pa_source_put(ca_source->pa_source);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (u)
+        pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+    coreaudio_sink *ca_sink;
+    coreaudio_source *ca_source;
+
+    pa_assert(m);
+
+    u = m->userdata;
+    pa_assert(u);
+
+    /* unlink sinks */
+    for (ca_sink = u->sinks; ca_sink; ca_sink = ca_sink->next)
+        if (ca_sink->pa_sink)
+            pa_sink_unlink(ca_sink->pa_sink);
+
+    /* unlink sources */
+    for (ca_source = u->sources; ca_source; ca_source = ca_source->next)
+        if (ca_source->pa_source)
+            pa_source_unlink(ca_source->pa_source);
+
+    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);
+        pa_asyncmsgq_unref(u->async_msgq);
+    }
+
+    /* free sinks */
+    for (ca_sink = u->sinks; ca_sink;) {
+        coreaudio_sink *next = ca_sink->next;
+
+        if (ca_sink->pa_sink)
+            pa_sink_unref(ca_sink->pa_sink);
+
+        pa_xfree(ca_sink->name);
+        pa_xfree(ca_sink);
+        ca_sink = next;
+    }
+
+    /* free sources */
+    for (ca_source = u->sources; ca_source;) {
+        coreaudio_source *next = ca_source->next;
+
+        if (ca_source->pa_source)
+            pa_source_unref(ca_source->pa_source);
+
+        pa_xfree(ca_source->name);
+        pa_xfree(ca_source);
+        ca_source = next;
+    }
+
+    if (u->proc_id) {
+        AudioDeviceStop(u->device_id, u->proc_id);
+        AudioDeviceDestroyIOProcID(u->device_id, u->proc_id);
+    }
+
+    AudioDeviceRemovePropertyListener(u->device_id, 0, 0, kAudioDevicePropertyStreamFormat, ca_stream_format_changed);
+
+    pa_xfree(u->device_name);
+    pa_xfree(u->vendor_name);
+    pa_rtpoll_free(u->rtpoll);
+    pa_card_free(u->card);
+
+    pa_xfree(u);
+}

commit 0ccc7afcd8cf77346568ffbf9a8b2e0b3ce2ea0e
Author: Daniel Mack <daniel at caiaq.de>
Date:   Thu Dec 10 10:32:57 2009 +0800

    osx: add native zeroconf implementation via Bonjour
    
    Avahi and dbus is too heavy for OSX just for the sake of publishing our
    services via mDNS/Zeroconf. Apple has its own Zeroconf implementation
    called Bonjour, and this patch adds a module that implements service
    announcement with that API.
    
    All data gathering is copied from module-zeroconf-publish.c, but
    unfortunately the code there is too specifically made for avahi, so I
    couldn't factor it out to reuse it.

diff --git a/configure.ac b/configure.ac
index 11af89b..6b540e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -423,8 +423,12 @@ if test "x$os_is_darwin" = "x1" ; then
 
     AC_MSG_RESULT([ok])
     AC_DEFINE([HAVE_CLOCK_GETTIME], 1, [Using clock_gettime() replacement])
+    HAVE_BONJOUR=1
 fi
 
+AC_SUBST(HAVE_BONJOUR)
+AM_CONDITIONAL([HAVE_BONJOUR], [test "x$HAVE_BONJOUR" = x1])
+
 #### Check for functions ####
 
 # ISO
diff --git a/src/Makefile.am b/src/Makefile.am
index fa5d170..0224cb3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1123,6 +1123,11 @@ modlibexec_LTLIBRARIES += \
 		module-zeroconf-discover.la
 endif
 
+if HAVE_BONJOUR
+modlibexec_LTLIBRARIES += \
+		module-bonjour-publish.la
+endif
+
 if HAVE_LIRC
 modlibexec_LTLIBRARIES += \
 		module-lirc.la
@@ -1231,6 +1236,7 @@ SYMDEF_FILES = \
 		modules/module-esound-sink-symdef.h \
 		modules/module-zeroconf-publish-symdef.h \
 		modules/module-zeroconf-discover-symdef.h \
+		modules/module-bonjour-publish-symdef.h \
 		modules/module-lirc-symdef.h \
 		modules/module-mmkbd-evdev-symdef.h \
 		modules/module-http-protocol-tcp-symdef.h \
@@ -1548,6 +1554,13 @@ module_zeroconf_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_zeroconf_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 module_zeroconf_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
 
+# Bonjour
+
+module_bonjour_publish_la_SOURCES = modules/module-bonjour-publish.c
+module_bonjour_publish_la_LDFLAGS = $(MODULE_LDFLAGS) \
+			-Wl,-framework -Wl,CoreFoundation -framework CoreFoundation
+module_bonjour_publish_la_LIBADD = libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+
 # LIRC
 
 module_lirc_la_SOURCES = modules/module-lirc.c
diff --git a/src/modules/module-bonjour-publish.c b/src/modules/module-bonjour-publish.c
new file mode 100644
index 0000000..81b5976
--- /dev/null
+++ b/src/modules/module-bonjour-publish.c
@@ -0,0 +1,516 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Daniel Mack
+  based on module-zeroconf-publish.c
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dns_sd.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/parseaddr.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/dynarray.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/avahi-wrap.h>
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/protocol-native.h>
+
+#include "module-bonjour-publish-symdef.h"
+
+PA_MODULE_AUTHOR("Daniel Mack");
+PA_MODULE_DESCRIPTION("Mac OS X Bonjour Service Publisher");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define SERVICE_TYPE_SINK "_pulse-sink._tcp"
+#define SERVICE_TYPE_SOURCE "_pulse-source._tcp"
+#define SERVICE_TYPE_SERVER "_pulse-server._tcp"
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+enum service_subtype {
+    SUBTYPE_HARDWARE,
+    SUBTYPE_VIRTUAL,
+    SUBTYPE_MONITOR
+};
+
+struct service {
+    struct userdata *userdata;
+    DNSServiceRef service;
+    DNSRecordRef rec, rec2;
+    char *service_name;
+    pa_object *device;
+    enum service_subtype subtype;
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    pa_hashmap *services;
+    char *service_name;
+
+    pa_hook_slot *sink_new_slot, *source_new_slot, *sink_unlink_slot, *source_unlink_slot, *sink_changed_slot, *source_changed_slot;
+
+    pa_native_protocol *native;
+    DNSServiceRef main_service;
+};
+
+static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_channel_map *ret_map, const char **ret_name, pa_proplist **ret_proplist, enum service_subtype *ret_subtype) {
+    pa_assert(s);
+    pa_assert(ret_ss);
+    pa_assert(ret_proplist);
+    pa_assert(ret_subtype);
+
+    if (pa_sink_isinstance(s->device)) {
+        pa_sink *sink = PA_SINK(s->device);
+
+        *ret_ss = sink->sample_spec;
+        *ret_map = sink->channel_map;
+        *ret_name = sink->name;
+        *ret_proplist = sink->proplist;
+        *ret_subtype = sink->flags & PA_SINK_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL;
+
+    } else if (pa_source_isinstance(s->device)) {
+        pa_source *source = PA_SOURCE(s->device);
+
+        *ret_ss = source->sample_spec;
+        *ret_map = source->channel_map;
+        *ret_name = source->name;
+        *ret_proplist = source->proplist;
+        *ret_subtype = source->monitor_of ? SUBTYPE_MONITOR : (source->flags & PA_SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL);
+
+    } else
+        pa_assert_not_reached();
+}
+
+static void txt_record_server_data(pa_core *c, TXTRecordRef *txt) {
+    char s[128];
+    char *t;
+
+    pa_assert(c);
+
+    TXTRecordSetValue(txt, "server-version", strlen(PACKAGE_NAME" "PACKAGE_VERSION), PACKAGE_NAME" "PACKAGE_VERSION);
+
+    t = pa_get_user_name_malloc();
+    TXTRecordSetValue(txt, "user-name", strlen(t), t);
+    pa_xfree(t);
+
+    t = pa_machine_id();
+    TXTRecordSetValue(txt, "machine-id", strlen(t), t);
+    pa_xfree(t);
+
+    t = pa_uname_string();
+    TXTRecordSetValue(txt, "uname", strlen(t), t);
+    pa_xfree(t);
+
+    t = pa_get_fqdn(s, sizeof(s));
+    TXTRecordSetValue(txt, "fqdn", strlen(t), t);
+
+    snprintf(s, sizeof(s), "0x%08x", c->cookie);
+    TXTRecordSetValue(txt, "cookie", strlen(s), s);
+}
+
+static void service_free(struct service *s);
+
+static void dns_service_register_reply(DNSServiceRef sdRef,
+                                       DNSServiceFlags flags,
+                                       DNSServiceErrorType errorCode,
+                                       const char *name,
+                                       const char *regtype,
+                                       const char *domain,
+                                       void *context) {
+    struct service *s = context;
+
+    pa_assert(s);
+
+    switch (errorCode) {
+    case kDNSServiceErr_NameConflict:
+        pa_log("DNS service reported kDNSServiceErr_NameConflict\n");
+        service_free(s);
+        break;
+
+    case kDNSServiceErr_NoError:
+    default:
+        break;
+    }
+}
+
+static uint16_t compute_port(struct userdata *u) {
+    pa_strlist *i;
+
+    pa_assert(u);
+
+    for (i = pa_native_protocol_servers(u->native); i; i = pa_strlist_next(i)) {
+        pa_parsed_address a;
+
+        if (pa_parse_address(pa_strlist_data(i), &a) >= 0 &&
+            (a.type == PA_PARSED_ADDRESS_TCP4 ||
+             a.type == PA_PARSED_ADDRESS_TCP6 ||
+             a.type == PA_PARSED_ADDRESS_TCP_AUTO) &&
+            a.port > 0) {
+
+            pa_xfree(a.path_or_host);
+            return a.port;
+        }
+
+        pa_xfree(a.path_or_host);
+    }
+
+    return PA_NATIVE_DEFAULT_PORT;
+}
+
+static int publish_service(struct service *s) {
+    int r = -1;
+    TXTRecordRef txt;
+    DNSServiceErrorType err;
+    const char *name = NULL, *t;
+    pa_proplist *proplist = NULL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    char cm[PA_CHANNEL_MAP_SNPRINT_MAX], tmp[64];
+    enum service_subtype subtype;
+
+    const char * const subtype_text[] = {
+        [SUBTYPE_HARDWARE] = "hardware",
+        [SUBTYPE_VIRTUAL] = "virtual",
+        [SUBTYPE_MONITOR] = "monitor"
+    };
+
+    pa_assert(s);
+
+    if (s->service) {
+        DNSServiceRefDeallocate(s->service);
+        s->service = NULL;
+    }
+
+    TXTRecordCreate(&txt, 0, NULL);
+
+    txt_record_server_data(s->userdata->core, &txt);
+
+    get_service_data(s, &ss, &map, &name, &proplist, &subtype);
+    TXTRecordSetValue(&txt, "device", strlen(name), name);
+
+    snprintf(tmp, sizeof(tmp), "%u", ss.rate);
+    TXTRecordSetValue(&txt, "rate", strlen(tmp), tmp);
+
+    snprintf(tmp, sizeof(tmp), "%u", ss.channels);
+    TXTRecordSetValue(&txt, "channels", strlen(tmp), tmp);
+
+    t = pa_sample_format_to_string(ss.format);
+    TXTRecordSetValue(&txt, "format", strlen(t), t);
+
+    t = pa_channel_map_snprint(cm, sizeof(cm), &map);
+    TXTRecordSetValue(&txt, "channel_map", strlen(t), t);
+
+    t = subtype_text[subtype];
+    TXTRecordSetValue(&txt, "subtype", strlen(t), t);
+
+    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_DESCRIPTION)))
+        TXTRecordSetValue(&txt, "description", strlen(t), t);
+    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_ICON_NAME)))
+        TXTRecordSetValue(&txt, "icon-name", strlen(t), t);
+    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_VENDOR_NAME)))
+        TXTRecordSetValue(&txt, "vendor-name", strlen(t), t);
+    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_PRODUCT_NAME)))
+        TXTRecordSetValue(&txt, "product-name", strlen(t), t);
+    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_CLASS)))
+        TXTRecordSetValue(&txt, "class", strlen(t), t);
+    if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_FORM_FACTOR)))
+        TXTRecordSetValue(&txt, "form-factor", strlen(t), t);
+
+    err = DNSServiceRegister(&s->service,
+                             0,         /* flags */
+                             kDNSServiceInterfaceIndexAny,
+                             s->service_name,
+                             pa_sink_isinstance(s->device) ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE,
+                             NULL,      /* domain */
+                             NULL,      /* host */
+                             compute_port(s->userdata),
+                             TXTRecordGetLength(&txt),
+                             TXTRecordGetBytesPtr(&txt),
+                             dns_service_register_reply, s);
+
+    if (err != kDNSServiceErr_NoError) {
+        pa_log("DNSServiceRegister() returned err %d", err);
+        goto finish;
+    }
+
+    pa_log_debug("Successfully registered Bonjour services for >%s<.", s->service_name);
+    return 0;
+
+finish:
+
+    /* Remove this service */
+    if (r < 0)
+        service_free(s);
+
+    TXTRecordDeallocate(&txt);
+
+    return r;
+}
+
+static struct service *get_service(struct userdata *u, pa_object *device) {
+    struct service *s;
+    char *hn, *un;
+    const char *n;
+
+    pa_assert(u);
+    pa_object_assert_ref(device);
+
+    if ((s = pa_hashmap_get(u->services, device)))
+        return s;
+
+    s = pa_xnew0(struct service, 1);
+    s->userdata = u;
+    s->device = device;
+
+    if (pa_sink_isinstance(device)) {
+        if (!(n = pa_proplist_gets(PA_SINK(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+            n = PA_SINK(device)->name;
+    } else {
+        if (!(n = pa_proplist_gets(PA_SOURCE(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+            n = PA_SOURCE(device)->name;
+    }
+
+    hn = pa_get_host_name_malloc();
+    un = pa_get_user_name_malloc();
+
+    s->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s: %s", un, hn, n), kDNSServiceMaxDomainName-1);
+
+    pa_xfree(un);
+    pa_xfree(hn);
+
+    pa_hashmap_put(u->services, s->device, s);
+
+    return s;
+}
+
+static void service_free(struct service *s) {
+    pa_assert(s);
+
+    pa_hashmap_remove(s->userdata->services, s->device);
+
+    if (s->service)
+        DNSServiceRefDeallocate(s->service);
+
+    pa_xfree(s->service_name);
+    pa_xfree(s);
+}
+
+static pa_bool_t shall_ignore(pa_object *o) {
+    pa_object_assert_ref(o);
+
+    if (pa_sink_isinstance(o))
+        return !!(PA_SINK(o)->flags & PA_SINK_NETWORK);
+
+    if (pa_source_isinstance(o))
+        return PA_SOURCE(o)->monitor_of || (PA_SOURCE(o)->flags & PA_SOURCE_NETWORK);
+
+    pa_assert_not_reached();
+}
+
+static pa_hook_result_t device_new_or_changed_cb(pa_core *c, pa_object *o, struct userdata *u) {
+    pa_assert(c);
+    pa_object_assert_ref(o);
+
+    if (!shall_ignore(o))
+        publish_service(get_service(u, o));
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t device_unlink_cb(pa_core *c, pa_object *o, struct userdata *u) {
+    struct service *s;
+
+    pa_assert(c);
+    pa_object_assert_ref(o);
+
+    if ((s = pa_hashmap_get(u->services, o)))
+        service_free(s);
+
+    return PA_HOOK_OK;
+}
+
+static int publish_main_service(struct userdata *u) {
+    DNSServiceErrorType err;
+    TXTRecordRef txt;
+
+    pa_assert(u);
+
+    if (u->main_service) {
+        DNSServiceRefDeallocate(u->main_service);
+        u->main_service = NULL;
+    }
+
+    TXTRecordCreate(&txt, 0, NULL);
+    txt_record_server_data(u->core, &txt);
+
+    err = DNSServiceRegister(&u->main_service,
+                             0, /* flags */
+                             kDNSServiceInterfaceIndexAny,
+                             u->service_name,
+                             SERVICE_TYPE_SERVER,
+                             NULL, /* domain */
+                             NULL, /* host */
+                             compute_port(u),
+                             TXTRecordGetLength(&txt),
+                             TXTRecordGetBytesPtr(&txt),
+                             NULL, NULL);
+
+    if (err != kDNSServiceErr_NoError) {
+        pa_log("%s(): DNSServiceRegister() returned err %d", __func__, err);
+        return err;
+    }
+
+    TXTRecordDeallocate(&txt);
+
+    return 0;
+}
+
+static int publish_all_services(struct userdata *u) {
+    pa_sink *sink;
+    pa_source *source;
+    uint32_t idx;
+
+    pa_assert(u);
+
+    pa_log_debug("Publishing services in Bonjour");
+
+    for (sink = PA_SINK(pa_idxset_first(u->core->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(u->core->sinks, &idx)))
+        if (!shall_ignore(PA_OBJECT(sink)))
+            publish_service(get_service(u, PA_OBJECT(sink)));
+
+    for (source = PA_SOURCE(pa_idxset_first(u->core->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(u->core->sources, &idx)))
+        if (!shall_ignore(PA_OBJECT(source)))
+            publish_service(get_service(u, PA_OBJECT(source)));
+
+    return publish_main_service(u);
+}
+
+static void unpublish_all_services(struct userdata *u) {
+    void *state = NULL;
+    struct service *s;
+
+    pa_assert(u);
+
+    pa_log_debug("Unpublishing services in Bonjour");
+
+    while ((s = pa_hashmap_iterate(u->services, &state, NULL)))
+        service_free(s);
+
+    if (u->main_service)
+        DNSServiceRefDeallocate(u->main_service);
+}
+
+int pa__init(pa_module*m) {
+
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    char *hn, *un;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->native = pa_native_protocol_get(u->core);
+
+    u->services = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u);
+    u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u);
+
+    un = pa_get_user_name_malloc();
+    hn = pa_get_host_name_malloc();
+    u->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s", un, hn), kDNSServiceMaxDomainName-1);
+    pa_xfree(un);
+    pa_xfree(hn);
+
+    publish_all_services(u);
+    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;
+
+    unpublish_all_services(u);
+
+    if (u->services)
+        pa_hashmap_free(u->services, NULL, NULL);
+
+    if (u->sink_new_slot)
+        pa_hook_slot_free(u->sink_new_slot);
+    if (u->source_new_slot)
+        pa_hook_slot_free(u->source_new_slot);
+    if (u->sink_changed_slot)
+        pa_hook_slot_free(u->sink_changed_slot);
+    if (u->source_changed_slot)
+        pa_hook_slot_free(u->source_changed_slot);
+    if (u->sink_unlink_slot)
+        pa_hook_slot_free(u->sink_unlink_slot);
+    if (u->source_unlink_slot)
+        pa_hook_slot_free(u->source_unlink_slot);
+
+    if (u->native)
+        pa_native_protocol_unref(u->native);
+
+    pa_xfree(u->service_name);
+    pa_xfree(u);
+}

commit f452c6cdd9e5717340807478d759ef5a03b55f1f
Author: Daniel Mack <daniel at caiaq.de>
Date:   Thu Dec 10 15:42:40 2009 +0800

    fix a number of warnings
    
    most of them were due to missing #ifdefs or wrong printf format type for
    [s]size_t.

diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index bfd5c11..79dd49a 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -466,6 +466,7 @@ static int parse_rtprio(const char *filename, unsigned line, const char *section
     return 0;
 }
 
+#ifdef HAVE_DBUS
 static int parse_server_type(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
     pa_daemon_conf *c = data;
 
@@ -481,6 +482,7 @@ static int parse_server_type(const char *filename, unsigned line, const char *se
 
     return 0;
 }
+#endif
 
 int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
     int r = -1;
@@ -666,12 +668,14 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
         [PA_LOG_ERROR] = "error"
     };
 
+#ifdef HAVE_DBUS
     static const char* const server_type_to_string[] = {
         [PA_SERVER_TYPE_UNSET] = "!!UNSET!!",
         [PA_SERVER_TYPE_USER] = "user",
         [PA_SERVER_TYPE_SYSTEM] = "system",
         [PA_SERVER_TYPE_NONE] = "none"
     };
+#endif
 
     pa_strbuf *s;
     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index d596c48..b64c51e 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -580,8 +580,8 @@ char *pa_strlcpy(char *b, const char *s, size_t l) {
 
 static int set_scheduler(int rtprio) {
     struct sched_param sp;
-    int r;
 #ifdef HAVE_DBUS
+    int r;
     DBusError error;
     DBusConnection *bus;
 
@@ -628,7 +628,7 @@ static int set_scheduler(int rtprio) {
 
     errno = -r;
 #else
-    errno = r;
+    errno = 0;
 #endif
 
     return -1;
diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c
index 0eca811..75e189c 100644
--- a/src/pulsecore/envelope.c
+++ b/src/pulsecore/envelope.c
@@ -597,7 +597,7 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {
         fs = pa_frame_size(&e->sample_spec);
         n = chunk->length;
 
-        pa_log_debug("Envelop position %d applying factor %d=%f, sample spec is %d, chunk's length is %d, fs is %d\n", e->x, linear_get_int(e, v), ((float) linear_get_int(e,v))/0x10000, e->sample_spec.format, n, fs);
+        pa_log_debug("Envelop position %zu applying factor %d=%f, sample spec is %d, chunk's length is %zu, fs is %zu\n", e->x, linear_get_int(e, v), ((float) linear_get_int(e,v))/0x10000, e->sample_spec.format, n, fs);
 
         switch (e->sample_spec.format) {
 
@@ -965,11 +965,10 @@ void pa_envelope_restart(pa_envelope* e) {
 }
 
 pa_bool_t pa_envelope_is_finished(pa_envelope* e) {
-    pa_assert(e);
-
     int v;
     pa_bool_t finished;
 
+    pa_assert(e);
     envelope_begin_read(e, &v);
     finished = (e->x >=  e->points[v].x[e->points[v].n_points-1]);
     envelope_commit_read(e, v);
@@ -978,11 +977,10 @@ pa_bool_t pa_envelope_is_finished(pa_envelope* e) {
 }
 
 int32_t pa_envelope_length(pa_envelope *e) {
-    pa_assert(e);
-
     int v;
     size_t size;
 
+    pa_assert(e);
     envelope_begin_read(e, &v);
     size = e->points[v].x[e->points[v].n_points-1] - e->points[v].x[0];
     envelope_commit_read(e, v);
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 35e3d4a..9b4edeb 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -779,11 +779,11 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
 
     if (!i->thread_info.ramp_info.envelope_dead) {
         i->thread_info.ramp_info.envelope_dying += chunk->length;
-        pa_log_debug("Envelope dying is %d, chunk length is %d, dead thresholder is %d\n", i->thread_info.ramp_info.envelope_dying,
+        pa_log_debug("Envelope dying is %d, chunk length is %zu, dead thresholder is %lu\n", i->thread_info.ramp_info.envelope_dying,
                 chunk->length,
                 i->sink->thread_info.max_rewind + pa_envelope_length(i->thread_info.ramp_info.envelope));
 
-        if (i->thread_info.ramp_info.envelope_dying >= (i->sink->thread_info.max_rewind + pa_envelope_length(i->thread_info.ramp_info.envelope))) {
+        if (i->thread_info.ramp_info.envelope_dying >= (int32_t) (i->sink->thread_info.max_rewind + pa_envelope_length(i->thread_info.ramp_info.envelope))) {
             pa_log_debug("RELEASE Envelop");
             i->thread_info.ramp_info.envelope_dead = TRUE;
             sink_input_release_envelope(i);
@@ -1767,12 +1767,14 @@ static void sink_input_rewind_ramp_info(pa_sink_input *i, size_t nbytes) {
     pa_assert(i);
 
     if (!i->thread_info.ramp_info.envelope_dead) {
-        pa_assert(i->thread_info.ramp_info.envelope);
+        int32_t envelope_length;
 
-        int32_t envelope_length = pa_envelope_length(i->thread_info.ramp_info.envelope);
+	pa_assert(i->thread_info.ramp_info.envelope);
+
+        envelope_length = pa_envelope_length(i->thread_info.ramp_info.envelope);
 
         if (i->thread_info.ramp_info.envelope_dying > envelope_length) {
-            if ((i->thread_info.ramp_info.envelope_dying - nbytes) < envelope_length) {
+            if ((int32_t) (i->thread_info.ramp_info.envelope_dying - nbytes) < envelope_length) {
                 pa_log_debug("Envelope Become Alive");
                 pa_envelope_rewind(i->thread_info.ramp_info.envelope, envelope_length - (i->thread_info.ramp_info.envelope_dying - nbytes));
                 i->thread_info.ramp_info.is_ramping = TRUE;

commit 937c587e7720ee27b36fe736a2aa1cfd233270ec
Author: Daniel Mack <daniel at caiaq.de>
Date:   Thu Dec 10 15:43:58 2009 +0800

    fix a number of warnings
    
    most of them were due to missing #ifdefs or wrong printf format type for
    [s]size_t.

diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index bfd5c11..79dd49a 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -466,6 +466,7 @@ static int parse_rtprio(const char *filename, unsigned line, const char *section
     return 0;
 }
 
+#ifdef HAVE_DBUS
 static int parse_server_type(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
     pa_daemon_conf *c = data;
 
@@ -481,6 +482,7 @@ static int parse_server_type(const char *filename, unsigned line, const char *se
 
     return 0;
 }
+#endif
 
 int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
     int r = -1;
@@ -666,12 +668,14 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
         [PA_LOG_ERROR] = "error"
     };
 
+#ifdef HAVE_DBUS
     static const char* const server_type_to_string[] = {
         [PA_SERVER_TYPE_UNSET] = "!!UNSET!!",
         [PA_SERVER_TYPE_USER] = "user",
         [PA_SERVER_TYPE_SYSTEM] = "system",
         [PA_SERVER_TYPE_NONE] = "none"
     };
+#endif
 
     pa_strbuf *s;
     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index d596c48..b64c51e 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -580,8 +580,8 @@ char *pa_strlcpy(char *b, const char *s, size_t l) {
 
 static int set_scheduler(int rtprio) {
     struct sched_param sp;
-    int r;
 #ifdef HAVE_DBUS
+    int r;
     DBusError error;
     DBusConnection *bus;
 
@@ -628,7 +628,7 @@ static int set_scheduler(int rtprio) {
 
     errno = -r;
 #else
-    errno = r;
+    errno = 0;
 #endif
 
     return -1;
diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c
index 0eca811..75e189c 100644
--- a/src/pulsecore/envelope.c
+++ b/src/pulsecore/envelope.c
@@ -597,7 +597,7 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {
         fs = pa_frame_size(&e->sample_spec);
         n = chunk->length;
 
-        pa_log_debug("Envelop position %d applying factor %d=%f, sample spec is %d, chunk's length is %d, fs is %d\n", e->x, linear_get_int(e, v), ((float) linear_get_int(e,v))/0x10000, e->sample_spec.format, n, fs);
+        pa_log_debug("Envelop position %zu applying factor %d=%f, sample spec is %d, chunk's length is %zu, fs is %zu\n", e->x, linear_get_int(e, v), ((float) linear_get_int(e,v))/0x10000, e->sample_spec.format, n, fs);
 
         switch (e->sample_spec.format) {
 
@@ -965,11 +965,10 @@ void pa_envelope_restart(pa_envelope* e) {
 }
 
 pa_bool_t pa_envelope_is_finished(pa_envelope* e) {
-    pa_assert(e);
-
     int v;
     pa_bool_t finished;
 
+    pa_assert(e);
     envelope_begin_read(e, &v);
     finished = (e->x >=  e->points[v].x[e->points[v].n_points-1]);
     envelope_commit_read(e, v);
@@ -978,11 +977,10 @@ pa_bool_t pa_envelope_is_finished(pa_envelope* e) {
 }
 
 int32_t pa_envelope_length(pa_envelope *e) {
-    pa_assert(e);
-
     int v;
     size_t size;
 
+    pa_assert(e);
     envelope_begin_read(e, &v);
     size = e->points[v].x[e->points[v].n_points-1] - e->points[v].x[0];
     envelope_commit_read(e, v);
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 35e3d4a..9b4edeb 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -779,11 +779,11 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
 
     if (!i->thread_info.ramp_info.envelope_dead) {
         i->thread_info.ramp_info.envelope_dying += chunk->length;
-        pa_log_debug("Envelope dying is %d, chunk length is %d, dead thresholder is %d\n", i->thread_info.ramp_info.envelope_dying,
+        pa_log_debug("Envelope dying is %d, chunk length is %zu, dead thresholder is %lu\n", i->thread_info.ramp_info.envelope_dying,
                 chunk->length,
                 i->sink->thread_info.max_rewind + pa_envelope_length(i->thread_info.ramp_info.envelope));
 
-        if (i->thread_info.ramp_info.envelope_dying >= (i->sink->thread_info.max_rewind + pa_envelope_length(i->thread_info.ramp_info.envelope))) {
+        if (i->thread_info.ramp_info.envelope_dying >= (int32_t) (i->sink->thread_info.max_rewind + pa_envelope_length(i->thread_info.ramp_info.envelope))) {
             pa_log_debug("RELEASE Envelop");
             i->thread_info.ramp_info.envelope_dead = TRUE;
             sink_input_release_envelope(i);
@@ -1767,12 +1767,14 @@ static void sink_input_rewind_ramp_info(pa_sink_input *i, size_t nbytes) {
     pa_assert(i);
 
     if (!i->thread_info.ramp_info.envelope_dead) {
-        pa_assert(i->thread_info.ramp_info.envelope);
+        int32_t envelope_length;
 
-        int32_t envelope_length = pa_envelope_length(i->thread_info.ramp_info.envelope);
+	pa_assert(i->thread_info.ramp_info.envelope);
+
+        envelope_length = pa_envelope_length(i->thread_info.ramp_info.envelope);
 
         if (i->thread_info.ramp_info.envelope_dying > envelope_length) {
-            if ((i->thread_info.ramp_info.envelope_dying - nbytes) < envelope_length) {
+            if ((int32_t) (i->thread_info.ramp_info.envelope_dying - nbytes) < envelope_length) {
                 pa_log_debug("Envelope Become Alive");
                 pa_envelope_rewind(i->thread_info.ramp_info.envelope, envelope_length - (i->thread_info.ramp_info.envelope_dying - nbytes));
                 i->thread_info.ramp_info.is_ramping = TRUE;

commit 6dfb1ee27e031528891696c83230d4513c55b554
Merge: 937c587 f452c6c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 5 22:52:57 2010 +0100

    Merge remote branch 'zonque/topic/osx'


commit aa9348441db34b787784711f19882d6c42fa315d
Author: huan zheng <huan.zheng.zbt at gmail.com>
Date:   Wed Jan 6 00:21:07 2010 +0100

    core: volume ramping fix
    
    Hi,
    I found that volume ramping is inside PA now.
    there's a minor fix here, it is a bug i found after the patch is submitted:
    
    line 1781 of sink-input.c :
    if ((i->thread_info.ramp_info.envelope_dying - nbytes) <= 0) {
    need to be changed to
    if ((i->thread_info.ramp_info.envelope_dying - (ssize_t) nbytes) <= 0) {
    
    otherwise this argument will never be negative since nbytes is of type
    size_t which is unsigned.
    Please change it when you have time, sorry if bring any inconvenience.  :)

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 9b4edeb..a490290 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1780,7 +1780,7 @@ static void sink_input_rewind_ramp_info(pa_sink_input *i, size_t nbytes) {
                 i->thread_info.ramp_info.is_ramping = TRUE;
             }
         } else if (i->thread_info.ramp_info.envelope_dying < envelope_length) {
-            if ((i->thread_info.ramp_info.envelope_dying - nbytes) <= 0) {
+            if ((i->thread_info.ramp_info.envelope_dying - (ssize_t) nbytes) <= 0) {
                 pa_log_debug("Envelope Restart");
                 pa_envelope_restart(i->thread_info.ramp_info.envelope);
             }

-- 
hooks/post-receive
PulseAudio Sound Server



More information about the pulseaudio-commits mailing list