[pulseaudio-commits] [SCM] PulseAudio Sound Server branch, stable-queue, updated. v0.9.21-109-g8036598

Colin Guthrie gitmailer-noreply at 0pointer.de
Wed Nov 17 15:58:23 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 stable-queue branch has been updated
      from  848dd378bb7fa53be7b88016335fa89d520f2ea3 (commit)

- Log -----------------------------------------------------------------
8036598 upnp: Implement the MediaServer2 D-Bus interface
70a060d combine: Only check if the sink is h/w etc. in automatic mode
33ea7b7 combine: Handle reappearing slave sinks in non-automatic mode.
34fd605 alsa: remove redundant call to snd_pcm_nonblock()
2c9c908 doxygen: Add 'See also' linking to the overview page
f2593da doxygen: Documentation improvements
14bc454 doxygen: Fix the "all" comments regarding volume helper functions.
b7303e2 doxygen: Fix documentation typos
352ae22 sink-input: Fix comment
-----------------------------------------------------------------------

Summary of changes:
 src/modules/alsa/alsa-util.c            |    2 -
 src/modules/module-combine.c            |   30 ++-
 src/modules/module-rygel-media-server.c |  662 +++++++++++++++++++++---------
 src/pulse/channelmap.h                  |    5 +-
 src/pulse/context.h                     |    5 +-
 src/pulse/glib-mainloop.h               |    9 +-
 src/pulse/introspect.h                  |    4 +-
 src/pulse/mainloop.h                    |    9 +-
 src/pulse/sample.h                      |    5 +-
 src/pulse/scache.h                      |    5 +-
 src/pulse/simple.h                      |    7 +-
 src/pulse/stream.h                      |   29 +-
 src/pulse/subscribe.h                   |    5 +-
 src/pulse/thread-mainloop.h             |   11 +-
 src/pulse/timeval.h                     |    4 +-
 src/pulse/volume.h                      |   26 +-
 src/pulsecore/sink-input.h              |    2 +-
 17 files changed, 570 insertions(+), 250 deletions(-)

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

commit 352ae22015452d730c32a7047675b820de2fbbf8
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Fri Oct 1 01:32:37 2010 +0100

    sink-input: Fix comment

diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 415a801..e666d4f 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -37,7 +37,7 @@ typedef struct pa_sink_input pa_sink_input;
 #include <pulsecore/core.h>
 
 typedef enum pa_sink_input_state {
-    PA_SINK_INPUT_INIT,         /*< The stream is not active yet, because pa_sink_put() has not been called yet */
+    PA_SINK_INPUT_INIT,         /*< The stream is not active yet, because pa_sink_input_put() has not been called yet */
     PA_SINK_INPUT_DRAINED,      /*< The stream stopped playing because there was no data to play */
     PA_SINK_INPUT_RUNNING,      /*< The stream is alive and kicking */
     PA_SINK_INPUT_CORKED,       /*< The stream was corked on user request */

commit b7303e2dd437a799bbdd4a74fb81a1472081f86a
Author: David Fries <david at fries.net>
Date:   Sat Sep 18 09:07:15 2010 -0500

    doxygen: Fix documentation typos

diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index 68cfc87..c7a8b1f 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -59,7 +59,7 @@
  * Note that even if a single object is requested, and not the entire list,
  * the terminating call will still be made.
  *
- * If an error occurs, the callback will be called without and information
+ * If an error occurs, the callback will be called without an information
  * structure and eol set to a negative value..
  *
  * Data members in the information structures are only valid during the
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index bc54a11..adf159c 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -486,8 +486,8 @@ int pa_stream_cancel_write(
  * data is not copied. If NULL, the data is copied into an internal
  * buffer. The client may freely seek around in the output buffer. For
  * most applications passing 0 and PA_SEEK_RELATIVE as arguments for
- * offset and seek should be useful. Afte ther write call succeeded
- * the write index will be a the position after where this chunk of
+ * offset and seek should be useful. After the write call succeeded
+ * the write index will be at the position after where this chunk of
  * data has been written to.
  *
  * As an optimization for avoiding needless memory copies you may call
@@ -739,8 +739,9 @@ pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[],
  * 0.9.11 */
 int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx);
 
-/** Return what has been set with pa_stream_set_monitor_stream()
- * ebfore. \since 0.9.11 */
+/** Return the sink input index previously set with
+ * pa_stream_set_monitor_stream().
+ * \since 0.9.11 */
 uint32_t pa_stream_get_monitor_stream(pa_stream *s);
 
 PA_C_DECL_END
diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h
index 3cea5d3..4201463 100644
--- a/src/pulse/timeval.h
+++ b/src/pulse/timeval.h
@@ -72,10 +72,10 @@ int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) PA_GCC_PURE
 /** Return the time difference between now and the specified timestamp */
 pa_usec_t pa_timeval_age(const struct timeval *tv);
 
-/** Add the specified time inmicroseconds to the specified timeval structure */
+/** Add the specified time in microseconds to the specified timeval structure */
 struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v);
 
-/** Subtract the specified time inmicroseconds to the specified timeval structure. \since 0.9.11 */
+/** Subtract the specified time in microseconds to the specified timeval structure. \since 0.9.11 */
 struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v);
 
 /** Store the specified uec value in the timeval struct. \since 0.9.7 */

commit 14bc4548cedec372d9ab6a6ed7be3b826b2a6a47
Author: David Fries <david at fries.net>
Date:   Tue Oct 12 18:22:27 2010 -0500

    doxygen: Fix the "all" comments regarding volume helper functions.
    
    Mostly change "Set the volume of all channels" to
    "Set the volume of the first n channels" as the first is incorrect,
    it doesn't set all the channels and doesn't explain what n was for.

diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index c964020..ed8dff9 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -81,12 +81,12 @@
  *                             structure are muted.
  * \li pa_cvolume_is_norm() - Tests if all channels of a pa_cvolume structure
  *                            are at a normal volume.
- * \li pa_cvolume_set() - Set all channels of a pa_cvolume structure to a
- *                        certain volume.
- * \li pa_cvolume_reset() - Set all channels of a pa_cvolume structure to a
- *                          normal volume.
- * \li pa_cvolume_mute() - Set all channels of a pa_cvolume structure to a
- *                         muted volume.
+ * \li pa_cvolume_set() - Set the first n channels of a pa_cvolume structure to
+ *                        a certain volume.
+ * \li pa_cvolume_reset() - Set the first n channels of a pa_cvolume structure
+ *                          to a normal volume.
+ * \li pa_cvolume_mute() - Set the first n channels of a pa_cvolume structure
+ *                         to a muted volume.
  * \li pa_cvolume_avg() - Return the average volume of all channels.
  * \li pa_cvolume_snprint() - Pretty print a pa_cvolume structure.
  */
@@ -129,13 +129,13 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) PA_GCC_PURE;
  * pa_cvolume_valid() will fail for it. \since 0.9.13 */
 pa_cvolume* pa_cvolume_init(pa_cvolume *a);
 
-/** Set the volume of all channels to PA_VOLUME_NORM */
+/** Set the volume of the first n channels to PA_VOLUME_NORM */
 #define pa_cvolume_reset(a, n) pa_cvolume_set((a), (n), PA_VOLUME_NORM)
 
-/** Set the volume of all channels to PA_VOLUME_MUTED */
+/** Set the volume of the first n channels to PA_VOLUME_MUTED */
 #define pa_cvolume_mute(a, n) pa_cvolume_set((a), (n), PA_VOLUME_MUTED)
 
-/** Set the volume of all channels to the specified parameter */
+/** Set the volume of the specified number of channels to the volume v */
 pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v);
 
 /** Maximum length of the strings returned by
@@ -259,7 +259,8 @@ pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST;
 /** Convert a volume to a decibel value (amplitude, not power). This is only valid for software volumes! */
 double pa_sw_volume_to_dB(pa_volume_t v) PA_GCC_CONST;
 
-/** Convert a linear factor to a volume. This is only valid for software volumes! */
+/** Convert a linear factor to a volume.  0.0 and less is muted while
+ * 1.0 is PA_VOLUME_NORM.  This is only valid for software volumes! */
 pa_volume_t pa_sw_volume_from_linear(double v) PA_GCC_CONST;
 
 /** Convert a volume to a linear factor. This is only valid for software volumes! */

commit f2593da3a36e1cb297a269d98205b4bdcde962f0
Author: David Fries <david at fries.net>
Date:   Tue Oct 12 20:05:47 2010 -0500

    doxygen: Documentation improvements
    
    stream.h, simple.h
    The words drain and flush are a little ambiguous, make it explicit as
    to what happens to any existing audio.
    
    *mainloop.h
    reword *_free and *_get_api for grammar

diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h
index 67aba27..fd173d0 100644
--- a/src/pulse/glib-mainloop.h
+++ b/src/pulse/glib-mainloop.h
@@ -57,8 +57,8 @@ pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c);
 void pa_glib_mainloop_free(pa_glib_mainloop* g);
 
 /** Return the abstract main loop API vtable for the GLIB main loop
-    object. No need of freeing the API as it is owned by the loop and
-    it is destroyed when this dies */
+    object. No need to free the API as it is owned by the loop
+    and is destroyed when the loop is freed. */
 pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g);
 
 PA_C_DECL_END
diff --git a/src/pulse/mainloop.h b/src/pulse/mainloop.h
index 63abd58..213f91d 100644
--- a/src/pulse/mainloop.h
+++ b/src/pulse/mainloop.h
@@ -109,8 +109,8 @@ int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval);
 int pa_mainloop_run(pa_mainloop *m, int *retval);
 
 /** Return the abstract main loop abstraction layer vtable for this
-    main loop. No need of freeing the API as it is owned by the loop
-    and it is destroyed when this dies */
+    main loop. No need to free the API as it is owned by the loop
+    and is destroyed when the loop is freed. */
 pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m);
 
 /** Shutdown the main loop */
diff --git a/src/pulse/simple.h b/src/pulse/simple.h
index 6f1ba41..8d0a388 100644
--- a/src/pulse/simple.h
+++ b/src/pulse/simple.h
@@ -140,7 +140,7 @@ int pa_simple_read(pa_simple *s, void*data, size_t bytes, int *error);
 /** Return the playback latency. */
 pa_usec_t pa_simple_get_latency(pa_simple *s, int *error);
 
-/** Flush the playback buffer. */
+/** Flush the playback buffer.  This discards any audio in the buffer. */
 int pa_simple_flush(pa_simple *s, int *error);
 
 PA_C_DECL_END
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index adf159c..7c53937 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -529,9 +529,10 @@ size_t pa_stream_writable_size(pa_stream *p);
 /** Return the number of bytes that may be read using pa_stream_peek()*/
 size_t pa_stream_readable_size(pa_stream *p);
 
-/** Drain a playback stream. Use this for notification when the buffer
- * is empty. Please note that only one drain operation per stream may
- * be issued at a time. */
+/** Drain a playback stream.  Use this for notification when the
+ * playback buffer is empty after playing all the audio in the buffer.
+ * Please note that only one drain operation per stream may be issued
+ * at a time. */
 pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
 
 /** Request a timing info structure update for a stream. Use
@@ -609,10 +610,10 @@ void pa_stream_set_buffer_attr_callback(pa_stream *p, pa_stream_notify_cb_t cb,
  * of the stream it will be created in corked state. */
 pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
 
-/** Flush the playback buffer of this stream. Most of the time you're
- * better off using the parameter delta of pa_stream_write() instead
- * of this function. Available on both playback and recording
- * streams. */
+/** Flush the playback buffer of this stream. This discards any audio
+ * in the buffer.  Most of the time you're better off using the parameter
+ * delta of pa_stream_write() instead of this function. Available on both
+ * playback and recording streams. */
 pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
 
 /** Reenable prebuffering as specified in the pa_buffer_attr
diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h
index 2cf496e..1c98d54 100644
--- a/src/pulse/thread-mainloop.h
+++ b/src/pulse/thread-mainloop.h
@@ -250,7 +250,7 @@ typedef struct pa_threaded_mainloop pa_threaded_mainloop;
 pa_threaded_mainloop *pa_threaded_mainloop_new(void);
 
 /** Free a threaded main loop object. If the event loop thread is
- * still running, it is terminated using pa_threaded_mainloop_stop()
+ * still running, terminate it with pa_threaded_mainloop_stop()
  * first. */
 void pa_threaded_mainloop_free(pa_threaded_mainloop* m);
 
@@ -300,8 +300,8 @@ void pa_threaded_mainloop_accept(pa_threaded_mainloop *m);
 int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m);
 
 /** Return the abstract main loop abstraction layer vtable for this
-    main loop. No need of freeing the API as it is owned by the loop
-    and it is destroyed when this dies */
+    main loop. No need to free the API as it is owned by the loop
+    and is destroyed when the loop is freed. */
 pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m);
 
 /** Returns non-zero when called from withing the event loop thread. \since 0.9.7 */

commit 2c9c908e856455dac36402a22e6575af131174ac
Author: David Fries <david at fries.net>
Date:   Sun Oct 3 13:41:27 2010 -0500

    doxygen: Add 'See also' linking to the overview page
    
    A good many of the header files are broken into a function
    reference page and an overview page.  These changes add
    a direct link from each function reference page to their
    overview page if one exists.

diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
index d1d5c8b..a6b93dd 100644
--- a/src/pulse/channelmap.h
+++ b/src/pulse/channelmap.h
@@ -65,7 +65,10 @@
  */
 
 /** \file
- * Constants and routines for channel mapping handling */
+ * Constants and routines for channel mapping handling
+ *
+ * See also \subpage channelmap
+ */
 
 PA_C_DECL_BEGIN
 
diff --git a/src/pulse/context.h b/src/pulse/context.h
index ecff58d..6e29325 100644
--- a/src/pulse/context.h
+++ b/src/pulse/context.h
@@ -145,7 +145,10 @@
 /** \file
  * Connection contexts for asynchrononous communication with a
  * server. A pa_context object wraps a connection to a PulseAudio
- * server using its native protocol. */
+ * server using its native protocol.
+ *
+ * See also \subpage async
+ */
 
 PA_C_DECL_BEGIN
 
diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h
index fd173d0..805d746 100644
--- a/src/pulse/glib-mainloop.h
+++ b/src/pulse/glib-mainloop.h
@@ -41,7 +41,10 @@
  */
 
 /** \file
- * GLIB main loop support */
+ * GLIB main loop support
+ *
+ * See also \subpage glib-mainloop
+ */
 
 PA_C_DECL_BEGIN
 
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index c7a8b1f..e6635d7 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -187,6 +187,8 @@
 /** \file
  *
  * Routines for daemon introspection.
+ *
+ * See also \subpage introspect
  */
 
 PA_C_DECL_BEGIN
diff --git a/src/pulse/mainloop.h b/src/pulse/mainloop.h
index 213f91d..19b17fa 100644
--- a/src/pulse/mainloop.h
+++ b/src/pulse/mainloop.h
@@ -71,7 +71,10 @@ struct pollfd;
  * function. Using the routines defined herein you may create a simple
  * main loop supporting the generic main loop abstraction layer as
  * defined in \ref mainloop-api.h. This implementation is thread safe
- * as long as you access the main loop object from a single thread only.*/
+ * as long as you access the main loop object from a single thread only.
+ *
+ * See also \subpage mainloop
+ */
 
 /** An opaque main loop object */
 typedef struct pa_mainloop pa_mainloop;
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
index 7a4a55a..c9e6fc4 100644
--- a/src/pulse/sample.h
+++ b/src/pulse/sample.h
@@ -105,7 +105,10 @@
  */
 
 /** \file
- * Constants and routines for sample type handling */
+ * Constants and routines for sample type handling
+ *
+ * See also \subpage sample
+ */
 
 PA_C_DECL_BEGIN
 
diff --git a/src/pulse/scache.h b/src/pulse/scache.h
index 31cf7b0..b4fcbd0 100644
--- a/src/pulse/scache.h
+++ b/src/pulse/scache.h
@@ -74,7 +74,10 @@
  */
 
 /** \file
- * All sample cache related routines */
+ * All sample cache related routines
+ *
+ * See also \subpage scache
+ */
 
 PA_C_DECL_BEGIN
 
diff --git a/src/pulse/simple.h b/src/pulse/simple.h
index 8d0a388..ba5e8fe 100644
--- a/src/pulse/simple.h
+++ b/src/pulse/simple.h
@@ -98,7 +98,10 @@
 /** \file
  * A simple but limited synchronous playback and recording
  * API. This is a synchronous, simplified wrapper around the standard
- * asynchronous API. */
+ * asynchronous API.
+ *
+ * See also \subpage simple
+ */
 
 /** \example pacat-simple.c
  * A simple playback tool using the simple API */
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index 7c53937..41e30da 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -310,7 +310,10 @@
  */
 
 /** \file
- * Audio streams for input, output and sample upload */
+ * Audio streams for input, output and sample upload
+ *
+ * See also \subpage streams
+ */
 
 PA_C_DECL_BEGIN
 
diff --git a/src/pulse/subscribe.h b/src/pulse/subscribe.h
index 44ed24a..f1e17ca 100644
--- a/src/pulse/subscribe.h
+++ b/src/pulse/subscribe.h
@@ -45,7 +45,10 @@
  */
 
 /** \file
- * Daemon introspection event subscription subsystem. */
+ * Daemon introspection event subscription subsystem.
+ *
+ * See also \subpage subscribe
+ */
 
 PA_C_DECL_BEGIN
 
diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h
index 1c98d54..f911228 100644
--- a/src/pulse/thread-mainloop.h
+++ b/src/pulse/thread-mainloop.h
@@ -239,7 +239,10 @@ PA_C_DECL_BEGIN
  * A thread based event loop implementation based on pa_mainloop. The
  * event loop is run in a helper thread in the background. A few
  * synchronization primitives are available to access the objects
- * attached to the event loop safely. */
+ * attached to the event loop safely.
+ *
+ * See also \subpage threaded_mainloop
+ */
 
 /** An opaque threaded main loop object */
 typedef struct pa_threaded_mainloop pa_threaded_mainloop;
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index ed8dff9..d98443b 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -92,7 +92,10 @@
  */
 
 /** \file
- * Constants and routines for volume handling */
+ * Constants and routines for volume handling
+ *
+ * See also \subpage volume
+ */
 
 PA_C_DECL_BEGIN
 

commit 34fd60567712cef97de04d32534f0ef841cb5cae
Author: Pierre-Louis Bossart <pierre-louis.bossart at intel.com>
Date:   Thu Nov 11 10:19:25 2010 -0600

    alsa: remove redundant call to snd_pcm_nonblock()
    
    The PCM handle is already opened with the SND_PCM_NONBLOCK flag.
    This additional call is useless.
    
    Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart at intel.com>

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 2d0d356..7ea26e1 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -399,8 +399,6 @@ success:
 
     ret = 0;
 
-    snd_pcm_nonblock(pcm_handle, 1);
-
 finish:
 
     return ret;

commit 33ea7b7816818113cf080e811547e1e76651109c
Author: Antti-Ville Jansson <antti-ville.jansson at digia.com>
Date:   Fri Nov 12 09:52:48 2010 +0200

    combine: Handle reappearing slave sinks in non-automatic mode.
    
    Earlier, if slave sinks were unlinked in non-automatic mode, their
    re-appearance was disregarded. Now they are added back to the list of outputs.
    
    Signed-off-by: Antti-Ville Jansson <antti-ville.jansson at digia.com>
    Reviewed-by: Tanu Kaskinen <tanu.kaskinen at digia.com>

diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index 5f94fdd..01d7a4e 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -47,6 +47,7 @@
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/core-error.h>
 #include <pulsecore/time-smoother.h>
+#include <pulsecore/strlist.h>
 
 #include "module-combine-symdef.h"
 
@@ -125,6 +126,8 @@ struct userdata {
     pa_bool_t automatic;
     pa_bool_t auto_desc;
 
+    pa_strlist *unlinked_slaves;
+
     pa_hook_slot *sink_put_slot, *sink_unlink_slot, *sink_state_changed_slot;
 
     pa_resample_method_t resample_method;
@@ -1026,11 +1029,23 @@ static pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *s, struct userdata
     pa_core_assert_ref(c);
     pa_sink_assert_ref(s);
     pa_assert(u);
-    pa_assert(u->automatic);
 
     if (!is_suitable_sink(u, s))
         return PA_HOOK_OK;
 
+    /* Check if the sink is a previously unlinked slave (non-automatic mode) */
+    if (!u->automatic) {
+        pa_strlist *l = u->unlinked_slaves;
+
+        while (l && !pa_streq(pa_strlist_data(l), s->name))
+            l = pa_strlist_next(l);
+
+        if (l)
+            u->unlinked_slaves = pa_strlist_remove(u->unlinked_slaves, s->name);
+        else
+            return PA_HOOK_OK;
+    }
+
     pa_log_info("Configuring new sink: %s", s->name);
     if (!(o = output_new(u, s))) {
         pa_log("Failed to create sink input on sink '%s'.", s->name);
@@ -1072,6 +1087,10 @@ static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userd
         return PA_HOOK_OK;
 
     pa_log_info("Unconfiguring sink: %s", s->name);
+
+    if (!u->automatic)
+        u->unlinked_slaves = pa_strlist_prepend(u->unlinked_slaves, s->name);
+
     output_free(o);
 
     return PA_HOOK_OK;
@@ -1297,10 +1316,9 @@ int pa__init(pa_module*m) {
                 goto fail;
             }
         }
-
-        u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_put_hook_cb, u);
     }
 
+    u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_put_hook_cb, u);
     u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) sink_unlink_hook_cb, u);
     u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_hook_cb, u);
 
@@ -1341,6 +1359,8 @@ void pa__done(pa_module*m) {
     if (!(u = m->userdata))
         return;
 
+    pa_strlist_free(u->unlinked_slaves);
+
     if (u->sink_put_slot)
         pa_hook_slot_free(u->sink_put_slot);
 

commit 70a060db92b09f8ab3651bbaff6d8f4864a0e826
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Wed Nov 17 23:43:36 2010 +0000

    combine: Only check if the sink is h/w etc. in automatic mode

diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index 01d7a4e..bcea229 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -1030,20 +1030,20 @@ static pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *s, struct userdata
     pa_sink_assert_ref(s);
     pa_assert(u);
 
-    if (!is_suitable_sink(u, s))
-        return PA_HOOK_OK;
-
-    /* Check if the sink is a previously unlinked slave (non-automatic mode) */
-    if (!u->automatic) {
+    if (u->automatic) {
+        if (!is_suitable_sink(u, s))
+            return PA_HOOK_OK;
+    } else {
+        /* Check if the sink is a previously unlinked slave (non-automatic mode) */
         pa_strlist *l = u->unlinked_slaves;
 
         while (l && !pa_streq(pa_strlist_data(l), s->name))
             l = pa_strlist_next(l);
 
-        if (l)
-            u->unlinked_slaves = pa_strlist_remove(u->unlinked_slaves, s->name);
-        else
+        if (!l)
             return PA_HOOK_OK;
+
+        u->unlinked_slaves = pa_strlist_remove(u->unlinked_slaves, s->name);
     }
 
     pa_log_info("Configuring new sink: %s", s->name);

commit 803659883decc539dabedd6359238a43fd12a039
Author: Stephen Moehle <stephen.moehle at gmail.com>
Date:   Sun Nov 14 12:31:15 2010 -0800

    upnp: Implement the MediaServer2 D-Bus interface
    
    This allows PulseAudio to work with versions of Rygel 0.7.1 and higher
    which only support MediaServer2:
     http://live.gnome.org/Rygel/MediaServer2Spec

diff --git a/src/modules/module-rygel-media-server.c b/src/modules/module-rygel-media-server.c
index 82bcd14..76b485c 100644
--- a/src/modules/module-rygel-media-server.c
+++ b/src/modules/module-rygel-media-server.c
@@ -52,33 +52,52 @@ PA_MODULE_AUTHOR("Lennart Poettering");
 PA_MODULE_DESCRIPTION("UPnP MediaServer Plugin for Rygel");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_USAGE(
-        "display_name=<UPnP Media Server name>");
+PA_MODULE_USAGE("display_name=<UPnP Media Server name>");
 
-/* This implements http://live.gnome.org/Rygel/MediaServerSpec */
+/* This implements http://live.gnome.org/Rygel/MediaServer2Spec */
 
-#define SERVICE_NAME "org.gnome.UPnP.MediaServer1.PulseAudio"
+#define SERVICE_NAME "org.gnome.UPnP.MediaServer2.PulseAudio"
 
-#define OBJECT_ROOT "/org/gnome/UPnP/MediaServer1/PulseAudio"
-#define OBJECT_SINKS "/org/gnome/UPnP/MediaServer1/PulseAudio/Sinks"
-#define OBJECT_SOURCES "/org/gnome/UPnP/MediaServer1/PulseAudio/Sources"
+#define OBJECT_ROOT "/org/gnome/UPnP/MediaServer2/PulseAudio"
+#define OBJECT_SINKS "/org/gnome/UPnP/MediaServer2/PulseAudio/Sinks"
+#define OBJECT_SOURCES "/org/gnome/UPnP/MediaServer2/PulseAudio/Sources"
 
 #define CONTAINER_INTROSPECT_XML_PREFIX                                 \
     DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
     "<node>"                                                            \
     " <!-- If you are looking for documentation make sure to check out" \
-    "      http://live.gnome.org/Rygel/MediaServerSpec -->"             \
-    " <interface name=\"org.gnome.UPnP.MediaContainer1\">"              \
+    "      http://live.gnome.org/Rygel/MediaServer2Spec -->"            \
+    " <interface name=\"org.gnome.UPnP.MediaContainer2\">"              \
+    "  <method name='ListChildren'>"                                    \
+    "   <arg direction='in' name='offset' type='u' />"                  \
+    "   <arg direction='in' name='max' type='u' />"                     \
+    "   <arg direction='in' name='filter' type='as' />"                 \
+    "   <arg direction='out' type='aa{sv}' />"                          \
+    "  </method>"                                                       \
+    "  <method name='ListContainers'>"                                  \
+    "   <arg direction='in' name='offset' type='u' />"                  \
+    "   <arg direction='in' name='max' type='u' />"                     \
+    "   <arg direction='in' name='filter' type='as' />"                 \
+    "   <arg direction='out' type='aa{sv}' />"                          \
+    "  </method>"                                                       \
+    "  <method name='ListItems'>"                                       \
+    "   <arg direction='in' name='offset' type='u' />"                  \
+    "   <arg direction='in' name='max' type='u' />"                     \
+    "   <arg direction='in' name='filter' type='as' />"                 \
+    "   <arg direction='out' type='aa{sv}' />"                          \
+    "  </method>"                                                       \
     "  <signal name=\"Updated\">"                                       \
     "   <arg name=\"path\" type=\"o\"/>"                                \
     "  </signal>"                                                       \
-    "  <property name=\"Items\" type=\"ao\" access=\"read\"/>"          \
+    "  <property name=\"ChildCount\" type=\"u\" access=\"read\"/>"      \
     "  <property name=\"ItemCount\" type=\"u\" access=\"read\"/>"       \
-    "  <property name=\"Containers\" type=\"ao\" access=\"read\"/>"     \
     "  <property name=\"ContainerCount\" type=\"u\" access=\"read\"/>"  \
+    "  <property name=\"Searchable\" type=\"b\" access=\"read\"/>"      \
     " </interface>"                                                     \
-    " <interface name=\"org.gnome.UPnP.MediaObject1\">"                 \
+    " <interface name=\"org.gnome.UPnP.MediaObject2\">"                 \
     "  <property name=\"Parent\" type=\"s\" access=\"read\"/>"          \
+    "  <property name=\"Type\" type=\"s\" access=\"read\"/>"            \
+    "  <property name=\"Path\" type=\"s\" access=\"read\"/>"            \
     "  <property name=\"DisplayName\" type=\"s\" access=\"read\"/>"     \
     " </interface>"                                                     \
     " <interface name=\"org.freedesktop.DBus.Properties\">"             \
@@ -111,14 +130,14 @@ PA_MODULE_USAGE(
     DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
     "<node>"                                                            \
     " <!-- If you are looking for documentation make sure to check out" \
-    "      http://live.gnome.org/Rygel/MediaProviderSpec -->"           \
-    " <interface name=\"org.gnome.UPnP.MediaItem1\">"                   \
+    "      http://live.gnome.org/Rygel/MediaProvider2Spec -->"          \
+    " <interface name=\"org.gnome.UPnP.MediaItem2\">"                   \
     "  <property name=\"URLs\" type=\"as\" access=\"read\"/>"           \
     "  <property name=\"MIMEType\" type=\"s\" access=\"read\"/>"        \
-    "  <property name=\"Type\" type=\"s\" access=\"read\"/>"            \
-    " </interface>"                                                     \
-    " <interface name=\"org.gnome.UPnP.MediaObject1\">"                 \
+    " <interface name=\"org.gnome.UPnP.MediaObject2\">"                 \
     "  <property name=\"Parent\" type=\"s\" access=\"read\"/>"          \
+    "  <property name=\"Type\" type=\"s\" access=\"read\"/>"            \
+    "  <property name=\"Path\" type=\"s\" access=\"read\"/>"            \
     "  <property name=\"DisplayName\" type=\"s\" access=\"read\"/>"     \
     " </interface>"                                                     \
     " <interface name=\"org.freedesktop.DBus.Properties\">"             \
@@ -159,6 +178,8 @@ struct userdata {
     pa_http_protocol *http;
 };
 
+static char *compute_url(const struct userdata *u, const char *name);
+
 static void send_signal(struct userdata *u, pa_source *s) {
     DBusMessage *m;
     const char *parent;
@@ -174,7 +195,7 @@ static void send_signal(struct userdata *u, pa_source *s) {
     else
         parent = OBJECT_SOURCES;
 
-    pa_assert_se(m = dbus_message_new_signal(parent, "org.gnome.UPnP.MediaContainer1", "Updated"));
+    pa_assert_se(m = dbus_message_new_signal(parent, "org.gnome.UPnP.MediaContainer2", "Updated"));
     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(u->bus), m, NULL));
 
     dbus_message_unref(m);
@@ -296,6 +317,69 @@ static void append_variant_unsigned(DBusMessage *m, DBusMessageIter *iter, unsig
     pa_assert_se(dbus_message_iter_close_container(iter, &sub));
 }
 
+static void append_variant_boolean(DBusMessage *m, DBusMessageIter *iter, dbus_bool_t b) {
+    DBusMessageIter _iter, sub;
+
+    pa_assert(m);
+
+    if (!iter) {
+        dbus_message_iter_init_append(m, &_iter);
+        iter = &_iter;
+    }
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "b", &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &b));
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_variant_urls(DBusMessage *m, DBusMessageIter *iter, const struct userdata *u, pa_sink *sink, pa_source *source) {
+    DBusMessageIter _iter, sub, array;
+    char *url;
+
+    pa_assert(m);
+    pa_assert(u);
+    pa_assert(sink || source);
+
+    if (!iter) {
+        dbus_message_iter_init_append(m, &_iter);
+        iter = &_iter;
+    }
+
+    url = compute_url(u, sink ? sink->monitor_source->name : source->name);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "as", &sub));
+    pa_assert_se(dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, "s", &array));
+    pa_assert_se(dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &url));
+    pa_assert_se(dbus_message_iter_close_container(&sub, &array));
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+
+    pa_xfree(url);
+}
+
+static void append_variant_mime_type(DBusMessage *m, DBusMessageIter *iter, pa_sink *sink, pa_source *source) {
+    char *mime_type;
+
+    pa_assert(sink || source);
+
+    if (sink)
+        mime_type = pa_sample_spec_to_mime_type_mimefy(&sink->sample_spec, &sink->channel_map);
+    else
+        mime_type = pa_sample_spec_to_mime_type_mimefy(&source->sample_spec, &source->channel_map);
+
+    append_variant_string(m, iter, mime_type);
+
+    pa_xfree(mime_type);
+}
+
+static void append_variant_item_display_name(DBusMessage *m, DBusMessageIter *iter, pa_sink *sink, pa_source *source) {
+    const char *display_name;
+
+    pa_assert(sink || source);
+
+    display_name = pa_strna(pa_proplist_gets(sink ? sink->proplist : source->proplist, PA_PROP_DEVICE_DESCRIPTION));
+    append_variant_string(m, iter, display_name);
+}
+
 static void append_property_dict_entry_object_array(DBusMessage *m, DBusMessageIter *iter, const char *name, const char *path[], unsigned n) {
     DBusMessageIter sub;
 
@@ -340,6 +424,195 @@ static void append_property_dict_entry_unsigned(DBusMessage *m, DBusMessageIter
     pa_assert_se(dbus_message_iter_close_container(iter, &sub));
 }
 
+static void append_property_dict_entry_boolean(DBusMessage *m, DBusMessageIter *iter, const char *name, dbus_bool_t b) {
+    DBusMessageIter sub;
+
+    pa_assert(iter);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name));
+    append_variant_boolean(m, &sub, b);
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_property_dict_entry_urls(DBusMessage *m, DBusMessageIter *iter, const struct userdata *u, pa_sink *sink, pa_source *source) {
+    DBusMessageIter sub;
+    const char *property_name = "URLs";
+
+    pa_assert(iter);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &property_name));
+    append_variant_urls(m, &sub, u, sink, source);
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_property_dict_entry_mime_type(DBusMessage *m, DBusMessageIter *iter, pa_sink *sink, pa_source *source) {
+    DBusMessageIter sub;
+    const char *property_name = "MIMEType";
+
+    pa_assert(iter);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &property_name));
+    append_variant_mime_type(m, &sub, sink, source);
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_property_dict_entry_item_display_name(DBusMessage *m, DBusMessageIter *iter, pa_sink *sink, pa_source *source) {
+    DBusMessageIter sub;
+    const char *property_name = "DisplayName";
+
+    pa_assert(iter);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub));
+    pa_assert_se(dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &property_name));
+    append_variant_item_display_name(m, &sub, sink, source);
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static pa_bool_t get_mediacontainer2_list_args(DBusMessage *m, unsigned *offset, unsigned *max_entries, char ***filter, int *filter_len) {
+    DBusError error;
+
+    dbus_error_init(&error);
+
+    pa_assert(m);
+    pa_assert(offset);
+    pa_assert(max_entries);
+    pa_assert(filter);
+
+    if (!dbus_message_get_args(m, &error, DBUS_TYPE_UINT32, offset, DBUS_TYPE_UINT32, max_entries, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, filter, filter_len, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
+        dbus_error_free(&error);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static unsigned get_sinks_or_sources_count(const char *path, const struct userdata *u) {
+    unsigned n, k;
+
+    n = pa_idxset_size(u->core->sinks);
+    k = pa_idxset_size(u->core->sources);
+    pa_assert(k >= n);
+
+    return pa_streq(path, OBJECT_SINKS) ? n : k - n;
+}
+
+static void append_sink_or_source_container_mediaobject2_properties(DBusMessage *r, DBusMessageIter *sub, const char *path) {
+    append_property_dict_entry_object(r, sub, "Parent", OBJECT_ROOT);
+    append_property_dict_entry_string(r, sub, "Type", "container");
+    append_property_dict_entry_object(r, sub, "Path", path);
+    append_property_dict_entry_string(r, sub, "DisplayName",
+                                      pa_streq(path, OBJECT_SINKS) ?
+                                      _("Output Devices") :
+                                      _("Input Devices"));
+}
+
+static void append_sink_or_source_container_properties(
+    DBusMessage *r, DBusMessageIter *iter,
+    const char *path, const struct userdata *user_data,
+    char * const * filter, int filter_len) {
+
+    DBusMessageIter sub;
+
+    pa_assert(r);
+    pa_assert(iter);
+    pa_assert(path);
+    pa_assert(filter);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
+
+    if (filter_len == 1 && (*filter)[0] == '*' && (*filter)[1] == '\0') {
+        append_sink_or_source_container_mediaobject2_properties(r, &sub, path);
+        append_property_dict_entry_unsigned(r, &sub, "ChildCount", get_sinks_or_sources_count(path, user_data));
+        append_property_dict_entry_boolean(r, &sub, "Searchable", FALSE);
+    }
+    else {
+        for (int i = 0; i < filter_len; ++i) {
+            const char *property_name = filter[i];
+            if (pa_streq(property_name, "Parent")) {
+                append_property_dict_entry_object(r, &sub, "Parent", OBJECT_ROOT);
+            }
+            else if (pa_streq(property_name, "Type")) {
+                append_property_dict_entry_string(r, &sub, "Type", "container");
+            }
+            else if (pa_streq(property_name, "Path")) {
+                append_property_dict_entry_object(r, &sub, "Path", path);
+            }
+            else if (pa_streq(property_name, "DisplayName")) {
+                append_property_dict_entry_string(r, &sub, "DisplayName",
+                                                  pa_streq(path, OBJECT_SINKS) ?
+                                                  _("Output Devices") :
+                                                  _("Input Devices"));
+            }
+            else if (pa_streq(property_name, "ChildCount")) {
+                append_property_dict_entry_unsigned(r, &sub, "ChildCount", get_sinks_or_sources_count(path, user_data));
+            }
+            else if (pa_streq(property_name, "Searchable")) {
+                append_property_dict_entry_boolean(r, &sub, "Searchable", FALSE);
+            }
+        }
+    }
+
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
+static void append_sink_or_source_item_mediaobject2_properties(DBusMessage *r, DBusMessageIter *sub, const char *path, pa_sink *sink, pa_source *source) {
+    append_property_dict_entry_object(r, sub, "Parent", sink ? OBJECT_SINKS : OBJECT_SOURCES);
+    append_property_dict_entry_string(r, sub, "Type", "audio");
+    append_property_dict_entry_object(r, sub, "Path", path);
+    append_property_dict_entry_item_display_name(r, sub, sink, source);
+}
+
+static void append_sink_or_source_item_properties(
+    DBusMessage *r, DBusMessageIter *iter,
+    const char *path, const struct userdata *user_data,
+    pa_sink *sink, pa_source *source,
+    char * const * filter, int filter_len) {
+
+    DBusMessageIter sub;
+
+    pa_assert(r);
+    pa_assert(iter);
+    pa_assert(path);
+    pa_assert(filter);
+    pa_assert(sink || source);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
+
+    if (filter_len == 1 && (*filter)[0] == '*' && (*filter)[1] == '\0') {
+        append_sink_or_source_item_mediaobject2_properties(r, &sub, path, sink, source);
+        append_property_dict_entry_urls(r, &sub, user_data, sink, source);
+        append_property_dict_entry_mime_type(r, &sub, sink, source);
+    }
+    else {
+        for (int i = 0; i < filter_len; ++i) {
+            const char *property_name = filter[i];
+            if (pa_streq(property_name, "Parent")) {
+                append_property_dict_entry_object(r, &sub, "Parent", sink ? OBJECT_SINKS : OBJECT_SOURCES);
+            }
+            else if (pa_streq(property_name, "Type")) {
+                append_property_dict_entry_string(r, &sub, "Type", "audio");
+            }
+            else if (pa_streq(property_name, "Path")) {
+                append_property_dict_entry_object(r, &sub, "Path", path);
+            }
+            else if (pa_streq(property_name, "DisplayName")) {
+                append_property_dict_entry_item_display_name(r, &sub, sink, source);
+            }
+            else if (pa_streq(property_name, "URLs")) {
+                append_property_dict_entry_urls(r, &sub, user_data, sink, source);
+            }
+            else if (pa_streq(property_name, "MIMEType")) {
+                append_property_dict_entry_mime_type(r, &sub, sink, source);
+            }
+        }
+    }
+
+    pa_assert_se(dbus_message_iter_close_container(iter, &sub));
+}
+
 static const char *array_root_containers[] = { OBJECT_SINKS, OBJECT_SOURCES };
 static const char *array_no_children[] = { };
 
@@ -349,50 +622,100 @@ static DBusHandlerResult root_handler(DBusConnection *c, DBusMessage *m, void *u
 
     pa_assert(u);
 
-    if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer1", "Containers")) {
+    if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "ChildCount")) {
         pa_assert_se(r = dbus_message_new_method_return(m));
-        append_variant_object_array(r, NULL, (const char**) array_root_containers, PA_ELEMENTSOF(array_root_containers));
+        append_variant_unsigned(r, NULL, PA_ELEMENTSOF(array_root_containers));
 
-    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer1", "ContainerCount")) {
+    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "ItemCount")) {
         pa_assert_se(r = dbus_message_new_method_return(m));
-        append_variant_unsigned(r, NULL, PA_ELEMENTSOF(array_root_containers));
+        append_variant_unsigned(r, NULL, PA_ELEMENTSOF(array_no_children));
 
-    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer1", "Items")) {
+    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "ContainerCount")) {
         pa_assert_se(r = dbus_message_new_method_return(m));
-        append_variant_object_array(r, NULL, array_no_children, PA_ELEMENTSOF(array_no_children));
+        append_variant_unsigned(r, NULL, PA_ELEMENTSOF(array_root_containers));
 
-    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer1", "ItemCount")) {
+    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "Searchable")) {
         pa_assert_se(r = dbus_message_new_method_return(m));
-        append_variant_unsigned(r, NULL, PA_ELEMENTSOF(array_no_children));
+        append_variant_boolean(r, NULL, FALSE);
 
-    } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaContainer1")) {
+    } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaContainer2")) {
         DBusMessageIter iter, sub;
 
         pa_assert_se(r = dbus_message_new_method_return(m));
         dbus_message_iter_init_append(r, &iter);
 
         pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
-        append_property_dict_entry_object_array(r, &sub, "Containers", array_root_containers, PA_ELEMENTSOF(array_root_containers));
-        append_property_dict_entry_unsigned(r, &sub, "ContainerCount", PA_ELEMENTSOF(array_root_containers));
-        append_property_dict_entry_object_array(r, &sub, "Items", array_no_children, PA_ELEMENTSOF(array_no_children));
+        append_property_dict_entry_unsigned(r, &sub, "ChildCount", PA_ELEMENTSOF(array_root_containers));
         append_property_dict_entry_unsigned(r, &sub, "ItemCount", PA_ELEMENTSOF(array_no_children));
+        append_property_dict_entry_unsigned(r, &sub, "ContainerCount", PA_ELEMENTSOF(array_root_containers));
+        append_property_dict_entry_boolean(r, &sub, "Searchable", FALSE);
+        pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
+
+    } else if (dbus_message_is_method_call(m, "org.gnome.UPnP.MediaContainer2", "ListChildren")
+        || dbus_message_is_method_call(m, "org.gnome.UPnP.MediaContainer2", "ListContainers")) {
+        DBusMessageIter iter, sub;
+        unsigned offset, max;
+        char ** filter;
+        int filter_len;
+
+        pa_assert_se(r = dbus_message_new_method_return(m));
+
+        dbus_message_iter_init_append(r, &iter);
+        pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "a{sv}", &sub));
+
+        if (get_mediacontainer2_list_args(m, &offset, &max, &filter, &filter_len)) {
+            unsigned end = (max != 0 && offset + max < PA_ELEMENTSOF(array_root_containers))
+                                ? max + offset
+                                : PA_ELEMENTSOF(array_root_containers);
+
+            for (unsigned i = offset; i < end; ++i) {
+                const char *container_path = array_root_containers[i];
+                append_sink_or_source_container_properties(r, &sub, container_path, u, filter, filter_len);
+            }
+
+            dbus_free_string_array(filter);
+        }
+
+        pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
+
+    } else if (dbus_message_is_method_call(m, "org.gnome.UPnP.MediaContainer2", "ListItems")) {
+        DBusMessageIter iter, sub;
+
+        pa_assert_se(r = dbus_message_new_method_return(m));
+
+        dbus_message_iter_init_append(r, &iter);
+        pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "a{sv}", &sub));
         pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
 
-    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject1", "Parent")) {
+    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Parent")) {
         pa_assert_se(r = dbus_message_new_method_return(m));
         append_variant_object(r, NULL, OBJECT_ROOT);
 
-    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject1", "DisplayName")) {
+    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Type")) {
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        append_variant_string(r, NULL, "container");
+
+    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Path")) {
+        const char *path = dbus_message_get_path(m);
+
+        pa_assert_se(r = dbus_message_new_method_return(m));
+        append_variant_object(r, NULL, path);
+
+    } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "DisplayName")) {
         pa_assert_se(r = dbus_message_new_method_return(m));
         append_variant_string(r, NULL, u->display_name);
 
-    } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaObject1")) {
+    } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaObject2")) {
         DBusMessageIter iter, sub;
+        const char *path = dbus_message_get_path(m);
 
         pa_assert_se(r = dbus_message_new_method_return(m));
         dbus_message_iter_init_append(r, &iter);
 
         pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
+        append_property_dict_entry_object(r, &sub, "Parent", OBJECT_ROOT);
+        append_property_dict_entry_string(r, &sub, "Type", "container");
+        append_property_dict_entry_object(r, &sub, "Path", path);
         append_property_dict_entry_string(r, &sub, "DisplayName", u->display_name);
         pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
 
@@ -416,7 +739,7 @@ static DBusHandlerResult root_handler(DBusConnection *c, DBusMessage *m, void *u
     return DBUS_HANDLER_RESULT_HANDLED;
 }
 
-static char *compute_url(struct userdata *u, const char *name) {
+static char *compute_url(const struct userdata *u, const char *name) {
     pa_strlist *i;
 
     pa_assert(u);
@@ -453,65 +776,6 @@ static char *compute_url(struct userdata *u, const char *name) {
     return pa_sprintf_malloc("http://@ADDRESS@:4714/listen/source/%s", name);
 }
 
-static char **child_array(struct userdata *u, const char *path, unsigned *n) {
-    unsigned m;
-    uint32_t idx;
-    char **array;
-
-    pa_assert(u);
-    pa_assert(path);
-    pa_assert(n);
-
-    if (pa_streq(path, OBJECT_SINKS))
-        m = pa_idxset_size(u->core->sinks);
-    else {
-        unsigned k;
-
-        m = pa_idxset_size(u->core->sources);
-        k = pa_idxset_size(u->core->sinks);
-
-        pa_assert(m >= k);
-
-        /* Subtract the monitor sources from the numbers of
-         * sources. There is one monitor source for each sink */
-        m -= k;
-    }
-
-    array = pa_xnew(char*, m);
-    *n = 0;
-
-    if (pa_streq(path, OBJECT_SINKS)) {
-        pa_sink *sink;
-
-        PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
-            pa_assert((*n) < m);
-            array[(*n)++] = pa_sprintf_malloc(OBJECT_SINKS "/%u", sink->index);
-        }
-    } else {
-        pa_source *source;
-
-        PA_IDXSET_FOREACH(source, u->core->sources, idx) {
-
-            if (!source->monitor_of) {
-                pa_assert((*n) < m);
-                array[(*n)++] = pa_sprintf_malloc(OBJECT_SOURCES "/%u", source->index);
-            }
-        }
-    }
-
-    pa_assert((*n) <= m);
-
-    return array;
-}
-
-static void free_child_array(char **array, unsigned n) {
-
-    for (; n >= 1; n--)
-        pa_xfree(array[n-1]);
-
-    pa_xfree(array);
-}
-
 static DBusHandlerResult sinks_and_sources_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
     struct userdata *u = userdata;
     DBusMessage *r = NULL;
@@ -525,66 +789,114 @@ static DBusHandlerResult sinks_and_sources_handler(DBusConnection *c, DBusMessag
 
         /* Container nodes */
 
-        if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer1", "Containers")) {
+        if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "ChildCount")
+            || message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "ItemCount")) {
             pa_assert_se(r = dbus_message_new_method_return(m));
-            append_variant_object_array(r, NULL, array_no_children, PA_ELEMENTSOF(array_no_children));
+            append_variant_unsigned(r, NULL, get_sinks_or_sources_count(path, u));
 
-        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer1", "ContainerCount")) {
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "ContainerCount")) {
             pa_assert_se(r = dbus_message_new_method_return(m));
-            append_variant_unsigned(r, NULL, PA_ELEMENTSOF(array_no_children));
+            append_variant_unsigned(r, NULL, 0);
 
-        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer1", "Items")) {
-            char ** array;
-            unsigned n;
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer2", "Searchable")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_boolean(r, NULL, FALSE);
 
-            array = child_array(u, path, &n);
+        } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaContainer2")) {
+            DBusMessageIter iter, sub;
+            unsigned item_count;
 
             pa_assert_se(r = dbus_message_new_method_return(m));
-            append_variant_object_array(r, NULL, (const char**) array, n);
+            dbus_message_iter_init_append(r, &iter);
 
-            free_child_array(array, n);
+            pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
 
-        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaContainer1", "ItemCount")) {
-            unsigned n, k;
+            item_count = get_sinks_or_sources_count(path, u);
 
-            n = pa_idxset_size(u->core->sinks);
-            k = pa_idxset_size(u->core->sources);
-            pa_assert(k >= n);
+            append_property_dict_entry_unsigned(r, &sub, "ChildCount", item_count);
+            append_property_dict_entry_unsigned(r, &sub, "ItemCount", item_count);
+            append_property_dict_entry_unsigned(r, &sub, "ContainerCount", 0);
+            append_property_dict_entry_boolean(r, &sub, "Searchable", FALSE);
 
-            pa_assert_se(r = dbus_message_new_method_return(m));
-            append_variant_unsigned(r, NULL,
-                                    pa_streq(path, OBJECT_SINKS) ? n : k - n);
+            pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
 
-        } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaContainer1")) {
+        } else if (dbus_message_is_method_call(m, "org.gnome.UPnP.MediaContainer2", "ListChildren")
+            || dbus_message_is_method_call(m, "org.gnome.UPnP.MediaContainer2", "ListItems")) {
             DBusMessageIter iter, sub;
-            char **array;
-            unsigned n, k;
+            unsigned offset, max;
+            char **filter;
+            int filter_len;
 
             pa_assert_se(r = dbus_message_new_method_return(m));
-            dbus_message_iter_init_append(r, &iter);
 
-            pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
-            append_property_dict_entry_object_array(r, &sub, "Containers", array_no_children, PA_ELEMENTSOF(array_no_children));
-            append_property_dict_entry_unsigned(r, &sub, "ContainerCount", 0);
+            dbus_message_iter_init_append(r, &iter);
+            pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "a{sv}", &sub));
+
+            if (get_mediacontainer2_list_args(m, &offset, &max, &filter, &filter_len)) {
+                unsigned end = (max != 0) ? max + offset : UINT_MAX;
+
+                if (pa_streq(path, OBJECT_SINKS)) {
+                    pa_sink *sink;
+                    char sink_path[sizeof(OBJECT_SINKS) + 32];
+                    char *path_end = sink_path + sizeof(OBJECT_SINKS);
+                    unsigned item_index = 0;
+                    uint32_t idx;
+
+                    strcpy(sink_path, OBJECT_SINKS "/");
+
+                    PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
+                        if (item_index >= offset && item_index < end) {
+                            sprintf(path_end, "%u", sink->index);
+                            append_sink_or_source_item_properties(r, &sub, sink_path, u, sink, NULL, filter, filter_len);
+                        }
+                        ++item_index;
+                    }
+                } else {
+                    pa_source *source;
+                    char source_path[sizeof(OBJECT_SOURCES) + 32];
+                    char *path_end = source_path + sizeof(OBJECT_SOURCES);
+                    unsigned item_index = 0;
+                    uint32_t idx;
+
+                    strcpy(source_path, OBJECT_SOURCES "/");
+
+                    PA_IDXSET_FOREACH(source, u->core->sources, idx)
+                        if (!source->monitor_of) {
+                            if (item_index >= offset && item_index < end) {
+                                sprintf(path_end, "%u", source->index);
+                                append_sink_or_source_item_properties(r, &sub, source_path, u, NULL, source, filter, filter_len);
+                            }
+                            ++item_index;
+                        }
+                }
+
+                dbus_free_string_array(filter);
+            }
 
-            array = child_array(u, path, &n);
-            append_property_dict_entry_object_array(r, &sub, "Items", (const char**) array, n);
-            free_child_array(array, n);
+            pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
 
-            n = pa_idxset_size(u->core->sinks);
-            k = pa_idxset_size(u->core->sources);
-            pa_assert(k >= n);
+        } else if (dbus_message_is_method_call(m, "org.gnome.UPnP.MediaContainer2", "ListContainers")) {
+            DBusMessageIter iter, sub;
 
-            append_property_dict_entry_unsigned(r, &sub, "ItemCount",
-                                                pa_streq(path, OBJECT_SINKS) ? n : k - n);
+            pa_assert_se(r = dbus_message_new_method_return(m));
 
+            dbus_message_iter_init_append(r, &iter);
+            pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "a{sv}", &sub));
             pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
 
-        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject1", "Parent")) {
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Parent")) {
             pa_assert_se(r = dbus_message_new_method_return(m));
             append_variant_object(r, NULL, OBJECT_ROOT);
 
-        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject1", "DisplayName")) {
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Type")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_string(r, NULL, "container");
+
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Path")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_object(r, NULL, path);
+
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "DisplayName")) {
             pa_assert_se(r = dbus_message_new_method_return(m));
             append_variant_string(r,
                                   NULL,
@@ -592,20 +904,14 @@ static DBusHandlerResult sinks_and_sources_handler(DBusConnection *c, DBusMessag
                                   _("Output Devices") :
                                   _("Input Devices"));
 
-        } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaObject1")) {
+        } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaObject2")) {
             DBusMessageIter iter, sub;
 
             pa_assert_se(r = dbus_message_new_method_return(m));
 
             dbus_message_iter_init_append(r, &iter);
-
             pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
-            append_property_dict_entry_object(m, &sub, "Parent", OBJECT_ROOT);
-            append_property_dict_entry_string(m, &sub, "DisplayName",
-                                              pa_streq(path, OBJECT_SINKS) ?
-                                              _("Output Devices") :
-                                              _("Input Devices"));
-            pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
+            append_sink_or_source_container_mediaobject2_properties(r, &sub, path);
 
         } else if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
             pa_strbuf *sb;
@@ -655,91 +961,49 @@ static DBusHandlerResult sinks_and_sources_handler(DBusConnection *c, DBusMessag
         if (!sink && !source)
             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
-        if (message_is_property_get(m, "org.gnome.UPnP.MediaObject1", "Parent")) {
+        if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Parent")) {
             pa_assert_se(r = dbus_message_new_method_return(m));
             append_variant_object(r, NULL, sink ? OBJECT_SINKS : OBJECT_SOURCES);
 
-        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject1", "DisplayName")) {
-            pa_assert_se(r = dbus_message_new_method_return(m));
-            append_variant_string(r, NULL, pa_strna(pa_proplist_gets(sink ? sink->proplist : source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
-
-        } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaObject1")) {
-            DBusMessageIter iter, sub;
-
-            pa_assert_se(r = dbus_message_new_method_return(m));
-            dbus_message_iter_init_append(r, &iter);
-
-            pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
-            append_property_dict_entry_object(r, &sub, "Parent", sink ? OBJECT_SINKS : OBJECT_SOURCES);
-            append_property_dict_entry_string(r, &sub, "DisplayName", pa_strna(pa_proplist_gets(sink ? sink->proplist : source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
-            pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
-
-        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaItem1", "Type")) {
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Type")) {
             pa_assert_se(r = dbus_message_new_method_return(m));
             append_variant_string(r, NULL, "audio");
 
-        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaItem1", "MIMEType")) {
-            char *t;
-
-            if (sink)
-                t = pa_sample_spec_to_mime_type_mimefy(&sink->sample_spec, &sink->channel_map);
-            else
-                t = pa_sample_spec_to_mime_type_mimefy(&source->sample_spec, &source->channel_map);
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "Path")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_object(r, NULL, path);
 
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaObject2", "DisplayName")) {
             pa_assert_se(r = dbus_message_new_method_return(m));
-            append_variant_string(r, NULL, t);
-            pa_xfree(t);
+            append_variant_item_display_name(r, NULL, sink, source);
 
-        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaItem1", "URLs")) {
-            DBusMessageIter iter, sub, array;
-            char *url;
+        } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaObject2")) {
+            DBusMessageIter iter, sub;
 
             pa_assert_se(r = dbus_message_new_method_return(m));
-
             dbus_message_iter_init_append(r, &iter);
 
-            url = compute_url(u, sink ? sink->monitor_source->name : source->name);
+            pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
+            append_sink_or_source_item_mediaobject2_properties(r, &sub, path, sink, source);
 
-            pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "as", &sub));
-            pa_assert_se(dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, "s", &array));
-            pa_assert_se(dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &url));
-            pa_assert_se(dbus_message_iter_close_container(&sub, &array));
-            pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaItem2", "MIMEType")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_mime_type(r, NULL, sink, source);
 
-            pa_xfree(url);
+        } else if (message_is_property_get(m, "org.gnome.UPnP.MediaItem2", "URLs")) {
+            pa_assert_se(r = dbus_message_new_method_return(m));
+            append_variant_urls(r, NULL, u, sink, source);
 
-        } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaItem1")) {
-            DBusMessageIter iter, sub, dict, variant, array;
-            char *url, *t;
-            const char *un = "URLs";
+        } else if (message_is_property_get_all(m, "org.gnome.UPnP.MediaItem2")) {
+            DBusMessageIter iter, sub;
 
             pa_assert_se(r = dbus_message_new_method_return(m));
             dbus_message_iter_init_append(r, &iter);
 
             pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub));
-            append_property_dict_entry_string(r, &sub, "Type", "audio");
-
-            if (sink)
-                t = pa_sample_spec_to_mime_type_mimefy(&sink->sample_spec, &sink->channel_map);
-            else
-                t = pa_sample_spec_to_mime_type_mimefy(&source->sample_spec, &source->channel_map);
-
-            append_property_dict_entry_string(r, &sub, "MIMEType", t);
-            pa_xfree(t);
-
-            pa_assert_se(dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, NULL, &dict));
-            pa_assert_se(dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &un));
-
-            url = compute_url(u, sink ? sink->monitor_source->name : source->name);
-
-            pa_assert_se(dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "as", &variant));
-            pa_assert_se(dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "s", &array));
-            pa_assert_se(dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &url));
-            pa_assert_se(dbus_message_iter_close_container(&variant, &array));
-            pa_assert_se(dbus_message_iter_close_container(&dict, &variant));
-            pa_assert_se(dbus_message_iter_close_container(&sub, &dict));
 
-            pa_xfree(url);
+            append_property_dict_entry_mime_type(r, &sub, sink, source);
+            append_property_dict_entry_urls(r, &sub, u, sink, source);
 
             pa_assert_se(dbus_message_iter_close_container(&iter, &sub));
 

-- 
hooks/post-receive
PulseAudio Sound Server



More information about the pulseaudio-commits mailing list