diff options
39 files changed, 988 insertions, 479 deletions
@@ -168,3 +168,16 @@ PA_COMMAND_GET_MODULE_INFO_LIST    remove bool auto_unload    add proplist at the end + +new messages: + +  PA_COMMAND_GET_CARD_INFO +  PA_COMMAND_GET_CARD_INFO_LIST +  PA_COMMAND_SET_CARD_PROFILE + +  PA_COMMAND_CLIENT_EVENT +  PA_COMMAND_PLAYBACK_STREAM_EVENT +  PA_COMMAND_RECORD_STREAM_EVENT + +  PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED +  PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED diff --git a/src/Makefile.am b/src/Makefile.am index a99e2756..0d4f53af 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -633,6 +633,12 @@ if OS_IS_WIN32  libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/dllmain.c  endif +if HAVE_DBUS +libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/dbus-util.c pulsecore/dbus-util.h +libpulsecommon_@PA_MAJORMINORMICRO@_la_CFLAGS += $(DBUS_CFLAGS) +libpulsecommon_@PA_MAJORMINORMICRO@_la_LIBADD += $(DBUS_LIBS) +endif +  ###################################  #         Client library          #  ################################### @@ -713,7 +719,7 @@ libpulse_la_SOURCES = \  		pulse/volume.c pulse/volume.h \  		pulse/xmalloc.c pulse/xmalloc.h -libpulse_la_CFLAGS = $(AM_CFLAGS) +libpulse_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)  libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) libpulsecommon-@PA_MAJORMINORMICRO@.la  libpulse_la_LDFLAGS = $(AM_LDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO) @@ -813,6 +819,13 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(X11_CFLAGS)  libpulsecore_@PA_MAJORMINORMICRO@_la_LDFLAGS += $(X11_LIBS)  endif + +if HAVE_DBUS +libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/dbus-shared.c pulsecore/dbus-shared.h +libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(DBUS_CFLAGS) +libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD += $(DBUS_LIBS) +endif +  # We split the foreign code off to not be annoyed by warnings we don't care about  noinst_LTLIBRARIES = libpulsecore-foreign.la @@ -902,7 +915,6 @@ libavahi_wrap_la_LIBADD = $(AM_LIBADD) $(AVAHI_CFLAGS) libpulsecore-@PA_MAJORMIN  if HAVE_DBUS  # Serveral module (e.g. libalsa-util.la)  modlibexec_LTLIBRARIES += \ -		libdbus-util.la \  		module-console-kit.la  endif @@ -1322,7 +1334,7 @@ endif  if HAVE_DBUS  libalsa_util_la_SOURCES += modules/reserve.h modules/reserve.c modules/reserve-wrap.c modules/reserve-wrap.h -libalsa_util_la_LIBADD += $(DBUS_LIBS) libdbus-util.la +libalsa_util_la_LIBADD += $(DBUS_LIBS)  libalsa_util_la_CFLAGS += $(DBUS_CFLAGS)  endif @@ -1476,20 +1488,14 @@ module_jack_source_la_LDFLAGS = $(MODULE_LDFLAGS)  module_jack_source_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la $(JACK_LIBS) libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_jack_source_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS) -# HAL/D-Bus -libdbus_util_la_SOURCES = modules/dbus-util.c modules/dbus-util.h -libdbus_util_la_LDFLAGS = -avoid-version -libdbus_util_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la -libdbus_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -  module_hal_detect_la_SOURCES = modules/module-hal-detect.c  module_hal_detect_la_LDFLAGS = $(MODULE_LDFLAGS) -module_hal_detect_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_hal_detect_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_hal_detect_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)  module_console_kit_la_SOURCES = modules/module-console-kit.c  module_console_kit_la_LDFLAGS = $(MODULE_LDFLAGS) -module_console_kit_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_console_kit_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_console_kit_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)  # GConf support @@ -1506,7 +1512,7 @@ gconf_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  # Bluetooth proximity  module_bluetooth_proximity_la_SOURCES = modules/bluetooth/module-bluetooth-proximity.c  module_bluetooth_proximity_la_LDFLAGS = $(MODULE_LDFLAGS) -module_bluetooth_proximity_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_bluetooth_proximity_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_bluetooth_proximity_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DPA_BT_PROXIMITY_HELPER=\"$(pulselibexecdir)/proximity-helper\"  proximity_helper_SOURCES = modules/bluetooth/proximity-helper.c @@ -1517,7 +1523,7 @@ proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  # Bluetooth sink / source  module_bluetooth_discover_la_SOURCES = modules/bluetooth/module-bluetooth-discover.c  module_bluetooth_discover_la_LDFLAGS = $(MODULE_LDFLAGS) -module_bluetooth_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libbluetooth-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_bluetooth_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libbluetooth-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)  libbluetooth_sbc_la_SOURCES = modules/bluetooth/sbc.c modules/bluetooth/sbc.h modules/bluetooth/sbc_tables.h modules/bluetooth/sbc_math.h modules/bluetooth/sbc_primitives.h modules/bluetooth/sbc_primitives.c modules/bluetooth/sbc_primitives_mmx.h modules/bluetooth/sbc_primitives_neon.h modules/bluetooth/sbc_primitives_mmx.c modules/bluetooth/sbc_primitives_neon.c @@ -1534,12 +1540,12 @@ BLUETOOTH_IPC_FILES = $(subst modules/bluetooth/,,$(libbluetooth_ipc_la_SOURCES)  libbluetooth_util_la_SOURCES = modules/bluetooth/bluetooth-util.c modules/bluetooth/bluetooth-util.h  libbluetooth_util_la_LDFLAGS = -avoid-version -libbluetooth_util_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la libdbus-util.la +libbluetooth_util_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  libbluetooth_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)  module_bluetooth_device_la_SOURCES = modules/bluetooth/module-bluetooth-device.c modules/bluetooth/rtp.h  module_bluetooth_device_la_LDFLAGS = $(MODULE_LDFLAGS) -module_bluetooth_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libbluetooth-util.la libbluetooth-ipc.la libbluetooth-sbc.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_bluetooth_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libbluetooth-util.la libbluetooth-ipc.la libbluetooth-sbc.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)  # Apple Airtunes/RAOP diff --git a/src/daemon/main.c b/src/daemon/main.c index 47ca2673..1d543481 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -87,6 +87,9 @@  #include <pulsecore/thread.h>  #include <pulsecore/once.h>  #include <pulsecore/shm.h> +#ifdef HAVE_DBUS +#include <pulsecore/dbus-shared.h> +#endif  #include "cmdline.h"  #include "cpulimit.h" @@ -330,6 +333,31 @@ static void set_all_rlimits(const pa_daemon_conf *conf) {  }  #endif +#ifdef HAVE_DBUS +static void register_org_pulseaudio(pa_core *c) +{ +    DBusError error; +    pa_dbus_connection *conn; + +    dbus_error_init(&error); +    if (!(conn = pa_dbus_bus_get(c, pa_in_system_mode() ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { +        pa_log_warn("Unable to contact DBUS: %s: %s", error.name, error.message); +        goto finish_dbus; +    } + +    if (dbus_bus_request_name (pa_dbus_connection_get(conn), "org.pulseaudio", 0, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) +        pa_log_debug("Got org.pulseaudio!"); +    else if (dbus_error_is_set(&error)) +        pa_log_warn("Unable to get org.pulseaudio: %s: %s", error.name, error.message); + +finish_dbus: +    if (conn) +        pa_dbus_connection_unref(conn); + +    dbus_error_free(&error); +} +#endif +  int main(int argc, char *argv[]) {      pa_core *c = NULL;      pa_strbuf *buf = NULL; @@ -997,6 +1025,10 @@ int main(int argc, char *argv[]) {      }  #endif +#ifdef HAVE_DBUS +    register_org_pulseaudio(c); +#endif +      pa_log_info(_("Daemon startup complete."));      retval = 0; diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 7c09553e..0296f64e 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1630,6 +1630,11 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca          u->use_tsched = use_tsched = FALSE;      } +    if (use_tsched && !pa_alsa_pcm_is_hw(u->pcm_handle)) { +        pa_log_info("Device is not a hardware device, disabling timer-based scheduling."); +        u->use_tsched = use_tsched = FALSE; +    } +      if (u->use_mmap)          pa_log_info("Successfully enabled mmap() mode."); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index dfd18702..ef365a21 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -1482,6 +1482,11 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p          u->use_tsched = use_tsched = FALSE;      } +    if (use_tsched && !pa_alsa_pcm_is_hw(u->pcm_handle)) { +        pa_log_info("Device is not a hardware device, disabling timer-based scheduling."); +        u->use_tsched = use_tsched = FALSE; +    } +      if (u->use_mmap)          pa_log_info("Successfully enabled mmap() mode."); diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 3f26aebe..2d0ca107 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -1716,10 +1716,11 @@ char *pa_alsa_get_driver_name(int card) {  char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm) {      int card; -      snd_pcm_info_t* info;      snd_pcm_info_alloca(&info); +    pa_assert(pcm); +      if (snd_pcm_info(pcm, info) < 0)          return NULL; @@ -1749,3 +1750,15 @@ char *pa_alsa_get_reserve_name(const char *device) {      return pa_sprintf_malloc("Audio%i", i);  } + +pa_bool_t pa_alsa_pcm_is_hw(snd_pcm_t *pcm) { +    snd_pcm_info_t* info; +    snd_pcm_info_alloca(&info); + +    pa_assert(pcm); + +    if (snd_pcm_info(pcm, info) < 0) +        return FALSE; + +    return snd_pcm_info_get_card(info) >= 0; +} diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h index fe0f71e0..68496d51 100644 --- a/src/modules/alsa/alsa-util.h +++ b/src/modules/alsa/alsa-util.h @@ -139,4 +139,6 @@ char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm);  char *pa_alsa_get_reserve_name(const char *device); +pa_bool_t pa_alsa_pcm_is_hw(snd_pcm_t *pcm); +  #endif diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c index 771afff5..94c1d315 100644 --- a/src/modules/bluetooth/bluetooth-util.c +++ b/src/modules/bluetooth/bluetooth-util.c @@ -25,7 +25,7 @@  #include <pulsecore/core-util.h>  #include <pulsecore/shared.h> -#include <modules/dbus-util.h> +#include <pulsecore/dbus-shared.h>  #include "bluetooth-util.h" diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 9fc1531e..f9800ac0 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -46,8 +46,7 @@  #include <pulsecore/time-smoother.h>  #include <pulsecore/rtclock.h>  #include <pulsecore/namereg.h> - -#include <modules/dbus-util.h> +#include <pulsecore/dbus-shared.h>  #include "module-bluetooth-device-symdef.h"  #include "ipc.h" diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c index 6f3dba12..788fee00 100644 --- a/src/modules/bluetooth/module-bluetooth-discover.c +++ b/src/modules/bluetooth/module-bluetooth-discover.c @@ -34,7 +34,7 @@  #include <pulsecore/macro.h>  #include <pulsecore/llist.h>  #include <pulsecore/core-util.h> -#include <modules/dbus-util.h> +#include <pulsecore/dbus-shared.h>  #include "module-bluetooth-discover-symdef.h"  #include "bluetooth-util.h" diff --git a/src/modules/bluetooth/module-bluetooth-proximity.c b/src/modules/bluetooth/module-bluetooth-proximity.c index c8d7b4d9..9993c8dc 100644 --- a/src/modules/bluetooth/module-bluetooth-proximity.c +++ b/src/modules/bluetooth/module-bluetooth-proximity.c @@ -42,8 +42,8 @@  #include <pulsecore/core-util.h>  #include <pulsecore/core-error.h>  #include <pulsecore/start-child.h> +#include <pulsecore/dbus-shared.h> -#include "../dbus-util.h"  #include "module-bluetooth-proximity-symdef.h"  PA_MODULE_AUTHOR("Lennart Poettering"); diff --git a/src/modules/hal-util.c b/src/modules/hal-util.c index 422ae4ec..e2a2d8d7 100644 --- a/src/modules/hal-util.c +++ b/src/modules/hal-util.c @@ -24,10 +24,10 @@  #endif  #include <pulsecore/log.h> +#include <pulsecore/dbus-shared.h>  #include <hal/libhal.h> -#include "dbus-util.h"  #include "hal-util.h"  int pa_hal_get_info(pa_core *core, pa_proplist *p, int card) { diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c index 3fba7ef6..a666073c 100644 --- a/src/modules/module-console-kit.c +++ b/src/modules/module-console-kit.c @@ -44,8 +44,8 @@  #include <pulsecore/namereg.h>  #include <pulsecore/core-scache.h>  #include <pulsecore/modargs.h> +#include <pulsecore/dbus-shared.h> -#include "dbus-util.h"  #include "module-console-kit-symdef.h"  PA_MODULE_AUTHOR("Lennart Poettering"); diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c index 9b0d71c9..0dd22cbd 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -45,10 +45,10 @@  #include <pulsecore/namereg.h>  #include <pulsecore/core-scache.h>  #include <pulsecore/modargs.h> +#include <pulsecore/dbus-shared.h>  #include <hal/libhal.h> -#include "dbus-util.h"  #include "module-hal-detect-symdef.h"  PA_MODULE_AUTHOR("Shahms King"); diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 00f0c63c..d8ddf184 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -135,13 +135,22 @@ enum {  /* Forward declaration */  static void sink_set_volume_cb(pa_sink *); -static void on_connection(PA_GCC_UNUSED int fd, void*userdata) { +static void on_connection(int fd, void*userdata) { +    int so_sndbuf = 0; +    socklen_t sl = sizeof(int);      struct userdata *u = userdata;      pa_assert(u);      pa_assert(u->fd < 0);      u->fd = fd; +    if (getsockopt(u->fd, SOL_SOCKET, SO_SNDBUF, &so_sndbuf, &sl) < 0) +        pa_log_warn("getsockopt(SO_SNDBUF) failed: %s", pa_cstrerror(errno)); +    else { +        pa_log_debug("SO_SNDBUF is %zu.", (size_t) so_sndbuf); +        pa_sink_set_max_request(u->sink, PA_MAX((size_t) so_sndbuf, u->block_size)); +    } +      /* Set the initial volume */      sink_set_volume_cb(u->sink); diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index c1488841..7f303f1f 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -145,6 +145,7 @@ static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t t  static void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);  static void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);  static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_stream_or_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);  static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {  #ifdef TUNNEL_SINK @@ -159,7 +160,10 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {      [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = command_suspended,      [PA_COMMAND_RECORD_STREAM_SUSPENDED] = command_suspended,      [PA_COMMAND_PLAYBACK_STREAM_MOVED] = command_moved, -    [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved +    [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved, +    [PA_COMMAND_PLAYBACK_STREAM_EVENT] = command_stream_or_client_event, +    [PA_COMMAND_RECORD_STREAM_EVENT] = command_stream_or_client_event, +    [PA_COMMAND_CLIENT_EVENT] = command_stream_or_client_event  };  struct userdata { @@ -196,8 +200,8 @@ struct userdata {      pa_bool_t remote_corked:1;      pa_bool_t remote_suspended:1; -    pa_usec_t transport_usec; -    pa_bool_t transport_usec_valid; +    pa_usec_t transport_usec; /* maintained in the main thread */ +    pa_usec_t thread_transport_usec; /* maintained in the IO thread */      uint32_t ignore_latency_before; @@ -222,6 +226,11 @@ struct userdata {  static void request_latency(struct userdata *u);  /* Called from main context */ +static void command_stream_or_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +    pa_log_debug("Got stream or client event."); +} + +/* Called from main context */  static void command_stream_killed(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {      struct userdata *u = userdata; @@ -261,11 +270,14 @@ static void command_suspended(pa_pdispatch *pd,  uint32_t command,  uint32_t tag      if (pa_tagstruct_getu32(t, &channel) < 0 ||          pa_tagstruct_get_boolean(t, &suspended) < 0 ||          !pa_tagstruct_eof(t)) { -        pa_log("Invalid packet"); + +        pa_log("Invalid packet.");          pa_module_unload_request(u->module, TRUE);          return;      } +    pa_log_debug("Server reports device suspend."); +  #ifdef TUNNEL_SINK      pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL);  #else @@ -278,13 +290,33 @@ static void command_suspended(pa_pdispatch *pd,  uint32_t command,  uint32_t tag  /* Called from main context */  static void command_moved(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {      struct userdata *u = userdata; +    uint32_t channel, di; +    const char *dn; +    pa_bool_t suspended;      pa_assert(pd);      pa_assert(t);      pa_assert(u);      pa_assert(u->pdispatch == pd); +    if (pa_tagstruct_getu32(t, &channel) < 0 || +        pa_tagstruct_getu32(t, &di) < 0 || +        pa_tagstruct_gets(t, &dn) < 0 || +        pa_tagstruct_get_boolean(t, &suspended) < 0) { + +        pa_log_error("Invalid packet."); +        pa_module_unload_request(u->module, TRUE); +        return; +    } +      pa_log_debug("Server reports a stream move."); + +#ifdef TUNNEL_SINK +    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL); +#else +    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL); +#endif +      request_latency(u);  } @@ -306,21 +338,21 @@ static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa  #endif  /* Called from IO thread context */ -static void stream_cork_within_thread(struct userdata *u, pa_bool_t cork) { +static void check_smoother_status(struct userdata *u, pa_bool_t past)  {      pa_usec_t x; -    pa_assert(u); -    if (u->remote_corked == cork) -        return; +    pa_assert(u); -    u->remote_corked = cork;      x = pa_rtclock_usec(); -    /* Correct by the time this needs to travel to the other side. -     * This is a valid thread-safe access, because the main thread is -     * waiting for us */ -    if (u->transport_usec_valid) -        x += u->transport_usec; +    /* Correct by the time the requested issued needs to travel to the +     * other side.  This is a valid thread-safe access, because the +     * main thread is waiting for us */ + +    if (past) +        x -= u->thread_transport_usec; +    else +        x += u->thread_transport_usec;      if (u->remote_suspended || u->remote_corked)          pa_smoother_pause(u->smoother, x); @@ -328,6 +360,17 @@ static void stream_cork_within_thread(struct userdata *u, pa_bool_t cork) {          pa_smoother_resume(u->smoother, x);  } +/* Called from IO thread context */ +static void stream_cork_within_thread(struct userdata *u, pa_bool_t cork) { +    pa_assert(u); + +    if (u->remote_corked == cork) +        return; + +    u->remote_corked = cork; +    check_smoother_status(u, FALSE); +} +  /* Called from main context */  static void stream_cork(struct userdata *u, pa_bool_t cork) {      pa_tagstruct *t; @@ -352,26 +395,13 @@ static void stream_cork(struct userdata *u, pa_bool_t cork) {  /* Called from IO thread context */  static void stream_suspend_within_thread(struct userdata *u, pa_bool_t suspend) { -    pa_usec_t x;      pa_assert(u);      if (u->remote_suspended == suspend)          return;      u->remote_suspended = suspend; - -    x = pa_rtclock_usec(); - -    /* Correct by the time this needed to travel from the other side. -     * This is a valid thread-safe access, because the main thread is -     * waiting for us */ -    if (u->transport_usec_valid) -        x -= u->transport_usec; - -    if (u->remote_suspended || u->remote_corked) -        pa_smoother_pause(u->smoother, x); -    else -        pa_smoother_resume(u->smoother, x); +    check_smoother_status(u, TRUE);  }  #ifdef TUNNEL_SINK @@ -446,13 +476,16 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse              y = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec); -            if (y > (pa_usec_t) offset || offset < 0) +            if (y > (pa_usec_t) offset)                  y -= (pa_usec_t) offset;              else                  y = 0;              pa_smoother_put(u->smoother, pa_rtclock_usec(), y); +            /* We can access this freely here, since the main thread is waiting for us */ +            u->thread_transport_usec = u->transport_usec; +              return 0;          } @@ -546,14 +579,13 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off              pa_usec_t y;              y = pa_bytes_to_usec((uint64_t) u->counter, &u->source->sample_spec); - -            if (offset >= 0 || y > (pa_usec_t) -offset) -                y += (pa_usec_t) offset; -            else -                y = 0; +            y += (pa_usec_t) offset;              pa_smoother_put(u->smoother, pa_rtclock_usec(), y); +            /* We can access this freely here, since the main thread is waiting for us */ +            u->thread_transport_usec = u->transport_usec; +              return 0;          }      } @@ -606,7 +638,7 @@ static void thread_func(void *userdata) {  #ifdef TUNNEL_SINK          if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) -            if (u->sink->thread_info.rewind_requested) +            if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))                  pa_sink_process_rewind(u->sink, 0);  #endif @@ -662,7 +694,7 @@ fail:  /* Called from main context */  static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      struct userdata *u = userdata; -    pa_usec_t sink_usec, source_usec, transport_usec = 0; +    pa_usec_t sink_usec, source_usec;      pa_bool_t playing;      int64_t write_index, read_index;      struct timeval local, remote, now; @@ -709,7 +741,6 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint      }      if (tag < u->ignore_latency_before) { -        request_latency(u);          return;      } @@ -725,7 +756,6 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint  #endif      } else          u->transport_usec = pa_timeval_diff(&now, &local)/2; -    u->transport_usec_valid = TRUE;      /* First, take the device's delay */  #ifdef TUNNEL_SINK @@ -745,9 +775,9 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint      /* Our measurements are already out of date, hence correct by the     *       * transport latency */  #ifdef TUNNEL_SINK -    delay -= (int64_t) transport_usec; +    delay -= (int64_t) u->transport_usec;  #else -    delay += (int64_t) transport_usec; +    delay += (int64_t) u->transport_usec;  #endif      /* Now correct by what we have have read/written since we requested the update */ @@ -786,8 +816,7 @@ static void request_latency(struct userdata *u) {      pa_tagstruct_putu32(t, tag = u->ctag++);      pa_tagstruct_putu32(t, u->channel); -    pa_gettimeofday(&now); -    pa_tagstruct_put_timeval(t, &now); +    pa_tagstruct_put_timeval(t, pa_gettimeofday(&now));      pa_pstream_send_tagstruct(u->pstream, t);      pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u, NULL); @@ -861,6 +890,7 @@ static void update_description(struct userdata *u) {  static void server_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {      struct userdata *u = userdata;      pa_sample_spec ss; +    pa_channel_map cm;      const char *server_name, *server_version, *user_name, *host_name, *default_sink_name, *default_source_name;      uint32_t cookie; @@ -882,7 +912,9 @@ static void server_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa          pa_tagstruct_get_sample_spec(t, &ss) < 0 ||          pa_tagstruct_gets(t, &default_sink_name) < 0 ||          pa_tagstruct_gets(t, &default_source_name) < 0 || -        pa_tagstruct_getu32(t, &cookie) < 0) { +        pa_tagstruct_getu32(t, &cookie) < 0 || +        (u->version >= 15 && +         pa_tagstruct_get_channel_map(t, &cm) < 0)) {          pa_log("Parse failure");          goto fail; @@ -963,6 +995,20 @@ static void sink_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_t          }      } +    if (u->version >= 15) { +        pa_volume_t base_volume; +        uint32_t state, n_volume_steps, card; + +        if (pa_tagstruct_get_volume(t, &base_volume) < 0 || +            pa_tagstruct_getu32(t, &state) < 0 || +            pa_tagstruct_getu32(t, &n_volume_steps) < 0 || +            pa_tagstruct_getu32(t, &card) < 0) { + +            pa_log("Parse failure"); +            goto fail; +        } +    } +      if (!pa_tagstruct_eof(t)) {          pa_log("Packet too long");          goto fail; @@ -1059,12 +1105,11 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag          pa_cvolume_equal(&volume, &u->sink->virtual_volume))          return; -    memcpy(&u->sink->virtual_volume, &volume, sizeof(pa_cvolume)); +    pa_sink_volume_changed(u->sink, &volume);      if (u->version >= 11) -        u->sink->muted = !!mute; +        pa_sink_mute_changed(u->sink, mute); -    pa_subscription_post(u->sink->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, u->sink->index);      return;  fail: @@ -1126,6 +1171,20 @@ static void source_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa          }      } +    if (u->version >= 15) { +        pa_volume_t base_volume; +        uint32_t state, n_volume_steps, card; + +        if (pa_tagstruct_get_volume(t, &base_volume) < 0 || +            pa_tagstruct_getu32(t, &state) < 0 || +            pa_tagstruct_getu32(t, &n_volume_steps) < 0 || +            pa_tagstruct_getu32(t, &card) < 0) { + +            pa_log("Parse failure"); +            goto fail; +        } +    } +      if (!pa_tagstruct_eof(t)) {          pa_log("Packet too long");          goto fail; @@ -1314,11 +1373,11 @@ static void create_stream_callback(pa_pdispatch *pd, uint32_t command,  uint32_t          if (pa_tagstruct_get_usec(t, &usec) < 0)              goto parse_error; -#ifdef TUNNEL_SINK -        pa_sink_set_latency_range(u->sink, usec + MIN_NETWORK_LATENCY_USEC, 0); -#else -        pa_source_set_latency_range(u->source, usec + MIN_NETWORK_LATENCY_USEC, 0); -#endif +/* #ifdef TUNNEL_SINK */ +/*         pa_sink_set_latency_range(u->sink, usec + MIN_NETWORK_LATENCY_USEC, 0); */ +/* #else */ +/*         pa_source_set_latency_range(u->source, usec + MIN_NETWORK_LATENCY_USEC, 0); */ +/* #endif */      }      if (!pa_tagstruct_eof(t)) @@ -1391,11 +1450,17 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t      pa_log_debug("Protocol version: remote %u, local %u", u->version, PA_PROTOCOL_VERSION);  #ifdef TUNNEL_SINK +    pa_proplist_setf(u->sink->proplist, "tunnel.remote_version", "%u", u->version); +    pa_sink_update_proplist(u->sink, 0, NULL); +      pa_snprintf(name, sizeof(name), "%s for %s@%s",                  u->sink_name,                  pa_get_user_name(un, sizeof(un)),                  pa_get_host_name(hn, sizeof(hn)));  #else +    pa_proplist_setf(u->source->proplist, "tunnel.remote_version", "%u", u->version); +    pa_source_update_proplist(u->source, 0, NULL); +      pa_snprintf(name, sizeof(name), "%s for %s@%s",                  u->source_name,                  pa_get_user_name(un, sizeof(un)), @@ -1409,9 +1474,9 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t      if (u->version >= 13) {          pa_proplist *pl;          pl = pa_proplist_new(); -        pa_init_proplist(pl);          pa_proplist_sets(pl, PA_PROP_APPLICATION_ID, "org.PulseAudio.PulseAudio");          pa_proplist_sets(pl, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION); +        pa_init_proplist(pl);          pa_tagstruct_put_proplist(reply, pl);          pa_proplist_free(pl);      } else @@ -1503,6 +1568,14 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t          pa_tagstruct_put_boolean(reply, TRUE); /* early rquests */      } +    if (u->version >= 15) { +#ifdef TUNNEL_SINK +        pa_tagstruct_put_boolean(reply, FALSE); /* muted_set */ +#endif +        pa_tagstruct_put_boolean(reply, FALSE); /* don't inhibit auto suspend */ +        pa_tagstruct_put_boolean(reply, FALSE); /* fail on suspend */ +    } +      pa_pstream_send_tagstruct(u->pstream, reply);      pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL); @@ -1559,7 +1632,6 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o      u->counter_delta += (int64_t) chunk->length;  } -  #endif  /* Called from main context */ @@ -1700,8 +1772,7 @@ int pa__init(pa_module*m) {      u->device_index = u->channel = PA_INVALID_INDEX;      u->time_event = NULL;      u->ignore_latency_before = 0; -    u->transport_usec = 0; -    u->transport_usec_valid = FALSE; +    u->transport_usec = u->thread_transport_usec = 0;      u->remote_suspended = u->remote_corked = FALSE;      u->counter = u->counter_delta = 0; @@ -1747,7 +1818,7 @@ int pa__init(pa_module*m) {      if (u->sink_name)          pa_proplist_sets(data.proplist, "tunnel.remote.sink", u->sink_name); -    u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL); +    u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL|PA_SINK_HW_MUTE_CTRL);      pa_sink_new_data_done(&data);      if (!u->sink) { @@ -1763,7 +1834,7 @@ int pa__init(pa_module*m) {      u->sink->refresh_volume = u->sink->refresh_muted = FALSE; -    pa_sink_set_latency_range(u->sink, MIN_NETWORK_LATENCY_USEC, 0); +/*     pa_sink_set_latency_range(u->sink, MIN_NETWORK_LATENCY_USEC, 0); */      pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);      pa_sink_set_rtpoll(u->sink, u->rtpoll); @@ -1797,7 +1868,7 @@ int pa__init(pa_module*m) {      u->source->set_state = source_set_state;      u->source->userdata = u; -    pa_source_set_latency_range(u->source, MIN_NETWORK_LATENCY_USEC, 0); +/*     pa_source_set_latency_range(u->source, MIN_NETWORK_LATENCY_USEC, 0); */      pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);      pa_source_set_rtpoll(u->source, u->rtpoll); diff --git a/src/modules/reserve-wrap.c b/src/modules/reserve-wrap.c index c956673b..1927342b 100644 --- a/src/modules/reserve-wrap.c +++ b/src/modules/reserve-wrap.c @@ -29,8 +29,7 @@  #include <pulsecore/core-error.h>  #include <pulsecore/core-util.h>  #include <pulsecore/shared.h> - -#include <modules/dbus-util.h> +#include <pulsecore/dbus-shared.h>  #include "reserve.h"  #include "reserve-wrap.h" diff --git a/src/pulse/context.c b/src/pulse/context.c index 00dffc25..9fb9e726 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -99,9 +99,12 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {      [PA_COMMAND_EXTENSION] = pa_command_extension,      [PA_COMMAND_PLAYBACK_STREAM_EVENT] = pa_command_stream_event,      [PA_COMMAND_RECORD_STREAM_EVENT] = pa_command_stream_event, -    [PA_COMMAND_CLIENT_EVENT] = pa_command_client_event +    [PA_COMMAND_CLIENT_EVENT] = pa_command_client_event, +    [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr, +    [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr  };  static void context_free(pa_context *c); +static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata);  pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {      return pa_context_new_with_proplist(mainloop, name, NULL); @@ -141,6 +144,8 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *      if (name)          pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name); +    c->no_fail = FALSE; +    c->system_bus = c->session_bus = NULL;      c->mainloop = mainloop;      c->client = NULL;      c->pstream = NULL; @@ -235,6 +240,16 @@ static void context_free(pa_context *c) {      context_unlink(c); +    if (c->system_bus) { +        dbus_connection_remove_filter(pa_dbus_wrap_connection_get(c->system_bus), filter_cb, c); +        pa_dbus_wrap_connection_free(c->system_bus); +    } + +    if (c->session_bus) { +        dbus_connection_remove_filter(pa_dbus_wrap_connection_get(c->session_bus), filter_cb, c); +        pa_dbus_wrap_connection_free(c->session_bus); +    } +      if (c->record_streams)          pa_dynarray_free(c->record_streams, NULL, NULL);      if (c->playback_streams) @@ -726,6 +741,32 @@ fail:  static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata); +static void track_pulseaudio_on_dbus(pa_context *c, DBusBusType type, pa_dbus_wrap_connection **conn) { +    DBusError error; + +    pa_assert(c); +    pa_assert(conn); + +    dbus_error_init(&error); +    if (!(*conn = pa_dbus_wrap_connection_new(c->mainloop, type, &error)) || dbus_error_is_set(&error)) { +        pa_log_warn("Unable to contact DBUS: %s: %s", error.name, error.message); +        goto finish; +    } + +    if (!dbus_connection_add_filter(pa_dbus_wrap_connection_get(*conn), filter_cb, c, NULL)) { +        pa_log_warn("Failed to add filter function"); +        goto finish; +    } + +    if (pa_dbus_add_matches( +                pa_dbus_wrap_connection_get(*conn), &error, +                "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',arg0='org.pulseaudio',arg1=''", NULL) < 0) +        pa_log_warn("Unable to track org.pulseaudio: %s: %s", error.name, error.message); + + finish: +    dbus_error_free(&error); +} +  static int try_next_connection(pa_context *c) {      char *u = NULL;      int r = -1; @@ -758,7 +799,14 @@ static int try_next_connection(pa_context *c) {              }  #endif -            pa_context_fail(c, PA_ERR_CONNECTIONREFUSED); +            if (c->no_fail) { +                if (!c->system_bus) +                    track_pulseaudio_on_dbus(c, DBUS_BUS_SYSTEM, &c->system_bus); +                if (!c->session_bus) +                    track_pulseaudio_on_dbus(c, DBUS_BUS_SESSION, &c->session_bus); +            } else +                pa_context_fail(c, PA_ERR_CONNECTIONREFUSED); +              goto finish;          } @@ -815,6 +863,34 @@ finish:      pa_context_unref(c);  } +static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) { +    pa_context *c = userdata; +    pa_bool_t is_session; + +    pa_assert(bus); +    pa_assert(message); +    pa_assert(c); + +    if (c->state != PA_CONTEXT_CONNECTING) +        goto finish; + +    is_session = bus == pa_dbus_wrap_connection_get(c->session_bus); +    pa_log_debug("Rock!! PulseAudio is baack on %s bus", is_session ? "session" : "system"); + +    if (is_session) { +        /* The user instance via PF_LOCAL */ +        c->server_list = prepend_per_user(c->server_list); +    } else { +        /* The system wide instance via PF_LOCAL */ +        c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET); +    } + +    try_next_connection(c); + +finish: +    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} +  int pa_context_connect(          pa_context *c,          const char *server, @@ -828,7 +904,7 @@ int pa_context_connect(      PA_CHECK_VALIDITY(c, !pa_detect_fork(), PA_ERR_FORKED);      PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE); -    PA_CHECK_VALIDITY(c, !(flags & ~PA_CONTEXT_NOAUTOSPAWN), PA_ERR_INVALID); +    PA_CHECK_VALIDITY(c, !(flags & ~(PA_CONTEXT_NOAUTOSPAWN|PA_CONTEXT_NOFAIL)), PA_ERR_INVALID);      PA_CHECK_VALIDITY(c, !server || *server, PA_ERR_INVALID);      if (!server) @@ -836,6 +912,7 @@ int pa_context_connect(      pa_context_ref(c); +    c->no_fail = flags & PA_CONTEXT_NOFAIL;      pa_assert(!c->server_list);      if (server) { diff --git a/src/pulse/context.h b/src/pulse/context.h index c32cf443..139d0e0b 100644 --- a/src/pulse/context.h +++ b/src/pulse/context.h @@ -207,9 +207,10 @@ pa_context_state_t pa_context_get_state(pa_context *c);  connect to the default server. This routine may but will not always  return synchronously on error. Use pa_context_set_state_callback() to  be notified when the connection is established. If flags doesn't have -PA_NOAUTOSPAWN set and no specific server is specified or accessible a -new daemon is spawned. If api is non-NULL, the functions specified in -the structure are used when forking a new child process. */ +PA_CONTEXT_NOAUTOSPAWN set and no specific server is specified or +accessible a new daemon is spawned. If api is non-NULL, the functions +specified in the structure are used when forking a new child +process. */  int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api);  /** Terminate the context connection immediately */ diff --git a/src/pulse/def.h b/src/pulse/def.h index 9418e96f..cae08942 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -109,13 +109,16 @@ typedef enum pa_operation_state {  /** Some special flags for contexts. */  typedef enum pa_context_flags { -    PA_CONTEXT_NOAUTOSPAWN = 1 +    PA_CONTEXT_NOAUTOSPAWN = 0x0001U,      /**< Disabled autospawning of the PulseAudio daemon if required */ +    PA_CONTEXT_NOFAIL = 0x0002U +    /**< Don't fail if the daemon is not available when pa_context_connect() is called, instead enter PA_CONTEXT_CONNECTING state and wait for the daemon to appear.  \since 0.9.15 */  } pa_context_flags_t;  /** \cond fulldocs */  /* Allow clients to check with #ifdef for those flags */  #define PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN +#define PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL  /** \endcond */  /** The direction of a pa_stream object */ @@ -624,7 +627,7 @@ typedef struct pa_timing_info {      /**< The configured latency for the sink. \since 0.9.11 */      pa_usec_t configured_source_usec; -    /**< The configured latency for * the source. \since 0.9.11 */ +    /**< The configured latency for the source. \since 0.9.11 */      int64_t since_underrun;      /**< Bytes that were handed to the sink since the last underrun diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 9646d8a6..8c242949 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -42,6 +42,7 @@  #include <pulsecore/hashmap.h>  #include <pulsecore/refcnt.h>  #include <pulsecore/time-smoother.h> +#include <pulsecore/dbus-util.h>  #include "client-conf.h" @@ -50,6 +51,10 @@  struct pa_context {      PA_REFCNT_DECLARE; +    pa_bool_t no_fail:1; +    pa_dbus_wrap_connection *system_bus; +    pa_dbus_wrap_connection *session_bus; +      pa_proplist *proplist;      pa_mainloop_api* mainloop; @@ -185,6 +190,8 @@ struct pa_stream {      void *started_userdata;      pa_stream_event_cb_t event_callback;      void *event_userdata; +    pa_stream_notify_cb_t buffer_attr_callback; +    void *buffer_attr_userdata;  };  typedef void (*pa_operation_cb_t)(void); @@ -213,6 +220,7 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, p  void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);  void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);  void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_command_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);  pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);  void pa_operation_done(pa_operation *o); diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 48e3b08e..bb53b19d 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -72,6 +72,8 @@ static void reset_callbacks(pa_stream *s) {      s->started_userdata = NULL;      s->event_callback = NULL;      s->event_userdata = NULL; +    s->buffer_attr_callback = NULL; +    s->buffer_attr_userdata = NULL;  }  pa_stream *pa_stream_new_with_proplist( @@ -396,7 +398,7 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, p      const char *dn;      pa_bool_t suspended;      uint32_t di; -    pa_usec_t usec; +    pa_usec_t usec = 0;      uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0;      pa_assert(pd); @@ -486,6 +488,80 @@ finish:      pa_context_unref(c);  } +void pa_command_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +    pa_context *c = userdata; +    pa_stream *s; +    uint32_t channel; +    pa_usec_t usec = 0; +    uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0; + +    pa_assert(pd); +    pa_assert(command == PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED || command == PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED); +    pa_assert(t); +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) >= 1); + +    pa_context_ref(c); + +    if (c->version < 15) { +        pa_context_fail(c, PA_ERR_PROTOCOL); +        goto finish; +    } + +    if (pa_tagstruct_getu32(t, &channel) < 0) { +        pa_context_fail(c, PA_ERR_PROTOCOL); +        goto finish; +    } + +    if (command == PA_COMMAND_RECORD_STREAM_MOVED) { +        if (pa_tagstruct_getu32(t, &maxlength) < 0 || +            pa_tagstruct_getu32(t, &fragsize) < 0 || +            pa_tagstruct_get_usec(t, &usec) < 0) { +            pa_context_fail(c, PA_ERR_PROTOCOL); +            goto finish; +        } +    } else { +        if (pa_tagstruct_getu32(t, &maxlength) < 0 || +            pa_tagstruct_getu32(t, &tlength) < 0 || +            pa_tagstruct_getu32(t, &prebuf) < 0 || +            pa_tagstruct_getu32(t, &minreq) < 0 || +            pa_tagstruct_get_usec(t, &usec) < 0) { +            pa_context_fail(c, PA_ERR_PROTOCOL); +            goto finish; +        } +    } + +    if (!pa_tagstruct_eof(t)) { +        pa_context_fail(c, PA_ERR_PROTOCOL); +        goto finish; +    } + +    if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED ? c->playback_streams : c->record_streams, channel))) +        goto finish; + +    if (s->state != PA_STREAM_READY) +        goto finish; + +    if (s->direction == PA_STREAM_RECORD) +        s->timing_info.configured_source_usec = usec; +    else +        s->timing_info.configured_sink_usec = usec; + +    s->buffer_attr.maxlength = maxlength; +    s->buffer_attr.fragsize = fragsize; +    s->buffer_attr.tlength = tlength; +    s->buffer_attr.prebuf = prebuf; +    s->buffer_attr.minreq = minreq; + +    request_auto_timing_update(s, TRUE); + +    if (s->buffer_attr_callback) +        s->buffer_attr_callback(s, s->buffer_attr_userdata); + +finish: +    pa_context_unref(c); +} +  void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_context *c = userdata;      pa_stream *s; @@ -1798,6 +1874,20 @@ void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *u      s->event_userdata = userdata;  } +void pa_stream_set_buffer_attr_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { +    pa_assert(s); +    pa_assert(PA_REFCNT_VALUE(s) >= 1); + +    if (pa_detect_fork()) +        return; + +    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) +        return; + +    s->buffer_attr_callback = cb; +    s->buffer_attr_userdata = userdata; +} +  void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      int success = 1; diff --git a/src/pulse/stream.h b/src/pulse/stream.h index e80bc65d..8e99a753 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -512,6 +512,13 @@ void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, vo   * control event is received.\since 0.9.15 */  void pa_stream_set_event_callback(pa_stream *p, pa_stream_event_cb_t cb, void *userdata); +/** Set the callback function that is called whenver the buffer + * attributes on the server side change. Please note that the buffer + * attributes can change when moving a stream to a different + * sink/source too, hence if you use this callback you should use + * pa_stream_set_moved_callback() as well. \since 0.9.15 */ +void pa_stream_set_buffer_attr_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); +  /** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. */  pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata); @@ -530,7 +537,7 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us   * temporarily. Available for playback streams only. */  pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata); -/** Rename the stream.*/ +/** Rename the stream. */  pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata);  /** Return the current playback/recording time. This is based on the diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index d4d407c6..4017cb68 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -175,7 +175,7 @@ static const struct command commands[] = {      { "suspend-sink",            pa_cli_command_suspend_sink,       "Suspend sink (args: index|name, bool)", 3},      { "suspend-source",          pa_cli_command_suspend_source,     "Suspend source (args: index|name, bool)", 3},      { "suspend",                 pa_cli_command_suspend,            "Suspend all sinks and all sources (args: bool)", 2}, -    { "set-card-profile",        pa_cli_command_card_profile,       "Change the profile of a card (aargs: index, name)", 3}, +    { "set-card-profile",        pa_cli_command_card_profile,       "Change the profile of a card (args: index, name)", 3},      { "set-log-level",           pa_cli_command_log_level,          "Change the log level (args: numeric level)", 2},      { "set-log-meta",            pa_cli_command_log_meta,           "Show source code location in log messages (args: bool)", 2},      { "set-log-time",            pa_cli_command_log_time,           "Show timestamps in log messages (args: bool)", 2}, diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 0d243ee6..e5d8a2f4 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -2617,7 +2617,7 @@ char *pa_realpath(const char *path) {          return NULL;      } -#ifndef __GLIBC__ +#if !defined(__GLIBC__) && !defined(__APPLE__)  #error "It's not clear whether this system supports realpath(..., NULL) like GNU libc does. If it doesn't we need a private version of realpath() here."  #endif diff --git a/src/pulsecore/dbus-shared.c b/src/pulsecore/dbus-shared.c new file mode 100644 index 00000000..b52c14cb --- /dev/null +++ b/src/pulsecore/dbus-shared.c @@ -0,0 +1,111 @@ +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006, 2009 Lennart Poettering +  Copyright 2006 Shams E. King + +  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 <stdarg.h> + +#include <pulse/xmalloc.h> +#include <pulse/timeval.h> +#include <pulsecore/log.h> +#include <pulsecore/shared.h> + +#include "dbus-shared.h" + +struct pa_dbus_connection { +    PA_REFCNT_DECLARE; + +    pa_dbus_wrap_connection *connection; +    pa_core *core; +    const char *property_name; +}; + +static pa_dbus_connection* pa_dbus_connection_new(pa_core *c, pa_dbus_wrap_connection *conn, const char *name) { +    pa_dbus_connection *pconn; + +    pconn = pa_xnew(pa_dbus_connection, 1); +    PA_REFCNT_INIT(pconn); +    pconn->core = c; +    pconn->property_name = name; +    pconn->connection = conn; + +    pa_shared_set(c, name, pconn); + +    return pconn; +} + +pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error) { + +    static const char *const prop_name[] = { +        [DBUS_BUS_SESSION] = "dbus-connection-session", +        [DBUS_BUS_SYSTEM] = "dbus-connection-system", +        [DBUS_BUS_STARTER] = "dbus-connection-starter" +    }; +    pa_dbus_wrap_connection *conn; +    pa_dbus_connection *pconn; + +    pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER); + +    if ((pconn = pa_shared_get(c, prop_name[type]))) +        return pa_dbus_connection_ref(pconn); + +    if (!(conn = pa_dbus_wrap_connection_new(c->mainloop, type, error))) +        return NULL; + +    pconn = pa_dbus_connection_new(c, conn, prop_name[type]); + +    return pconn; +} + +DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c){ +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) > 0); +    pa_assert(c->connection); + +    return pa_dbus_wrap_connection_get(c->connection); +} + +void pa_dbus_connection_unref(pa_dbus_connection *c) { +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) > 0); + +    if (PA_REFCNT_DEC(c) > 0) +        return; + +    /* already disconnected, just free */ +    pa_shared_remove(c->core, c->property_name); +    pa_xfree(c); +} + +pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) { +    pa_assert(c); +    pa_assert(PA_REFCNT_VALUE(c) > 0); + +    PA_REFCNT_INC(c); + +    return c; +} + + + diff --git a/src/pulsecore/dbus-shared.h b/src/pulsecore/dbus-shared.h new file mode 100644 index 00000000..4c154552 --- /dev/null +++ b/src/pulsecore/dbus-shared.h @@ -0,0 +1,42 @@ +#ifndef foodbussharedhfoo +#define foodbussharedhfoo + +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006, 2009 Lennart Poettering +  Copyright 2006 Shams E. King + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2.1 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#include <dbus/dbus.h> + +#include <pulsecore/core.h> +#include <pulsecore/dbus-util.h> + +typedef struct pa_dbus_connection pa_dbus_connection; + +/* return a pa_dbus_connection of the specified type for the given core, + * like dbus_bus_get(), but integrates the connection with the pa_core */ +pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error); + +DBusConnection* pa_dbus_connection_get(pa_dbus_connection *conn); + +pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *conn); +void pa_dbus_connection_unref(pa_dbus_connection *conn); + +#endif diff --git a/src/modules/dbus-util.c b/src/pulsecore/dbus-util.c index e2d45803..d712bff3 100644 --- a/src/modules/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -29,16 +29,12 @@  #include <pulse/xmalloc.h>  #include <pulse/timeval.h>  #include <pulsecore/log.h> -#include <pulsecore/shared.h>  #include "dbus-util.h" -struct pa_dbus_connection { -    PA_REFCNT_DECLARE; - -    pa_core *core; +struct pa_dbus_wrap_connection { +    pa_mainloop_api *mainloop;      DBusConnection *connection; -    const char *property_name;      pa_defer_event* dispatch_event;  }; @@ -53,20 +49,20 @@ static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata)  /* DBusDispatchStatusFunction callback for the pa mainloop */  static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) { -    pa_dbus_connection *c = userdata; +    pa_dbus_wrap_connection *c = userdata;      pa_assert(c);      switch(status) {          case DBUS_DISPATCH_COMPLETE: -            c->core->mainloop->defer_enable(c->dispatch_event, 0); +            c->mainloop->defer_enable(c->dispatch_event, 0);              break;          case DBUS_DISPATCH_DATA_REMAINS:          case DBUS_DISPATCH_NEED_MEMORY:          default: -            c->core->mainloop->defer_enable(c->dispatch_event, 1); +            c->mainloop->defer_enable(c->dispatch_event, 1);              break;      }  } @@ -135,7 +131,7 @@ static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struc  /* DBusAddWatchFunction callback for pa mainloop */  static dbus_bool_t add_watch(DBusWatch *watch, void *data) { -    pa_core *c = PA_CORE(data); +    pa_dbus_wrap_connection *c = data;      pa_io_event *ev;      pa_assert(watch); @@ -157,7 +153,7 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *data) {  /* DBusRemoveWatchFunction callback for pa mainloop */  static void remove_watch(DBusWatch *watch, void *data) { -    pa_core *c = PA_CORE(data); +    pa_dbus_wrap_connection *c = data;      pa_io_event *ev;      pa_assert(watch); @@ -169,11 +165,11 @@ static void remove_watch(DBusWatch *watch, void *data) {  /* DBusWatchToggledFunction callback for pa mainloop */  static void toggle_watch(DBusWatch *watch, void *data) { -    pa_core *c = PA_CORE(data); +    pa_dbus_wrap_connection *c = data;      pa_io_event *ev;      pa_assert(watch); -    pa_core_assert_ref(c); +    pa_assert(c);      pa_assert_se(ev = dbus_watch_get_data(watch)); @@ -183,7 +179,7 @@ static void toggle_watch(DBusWatch *watch, void *data) {  /* DBusAddTimeoutFunction callback for pa mainloop */  static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) { -    pa_core *c = PA_CORE(data); +    pa_dbus_wrap_connection *c = data;      pa_time_event *ev;      struct timeval tv; @@ -205,7 +201,7 @@ static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {  /* DBusRemoveTimeoutFunction callback for pa mainloop */  static void remove_timeout(DBusTimeout *timeout, void *data) { -    pa_core *c = PA_CORE(data); +    pa_dbus_wrap_connection *c = data;      pa_time_event *ev;      pa_assert(timeout); @@ -217,7 +213,7 @@ static void remove_timeout(DBusTimeout *timeout, void *data) {  /* DBusTimeoutToggledFunction callback for pa mainloop */  static void toggle_timeout(DBusTimeout *timeout, void *data) { -    pa_core *c = PA_CORE(data); +    pa_dbus_wrap_connection *c = data;      pa_time_event *ev;      pa_assert(timeout); @@ -237,95 +233,59 @@ static void toggle_timeout(DBusTimeout *timeout, void *data) {  }  static void wakeup_main(void *userdata) { -    pa_dbus_connection *c = userdata; +    pa_dbus_wrap_connection *c = userdata;      pa_assert(c);      /* this will wakeup the mainloop and dispatch events, although       * it may not be the cleanest way of accomplishing it */ -    c->core->mainloop->defer_enable(c->dispatch_event, 1); +    c->mainloop->defer_enable(c->dispatch_event, 1);  } -static pa_dbus_connection* pa_dbus_connection_new(pa_core* c, DBusConnection *conn, const char* name) { -    pa_dbus_connection *pconn; +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBusType type, DBusError *error) { +    DBusConnection *conn; +    pa_dbus_wrap_connection *pconn = NULL; -    pconn = pa_xnew(pa_dbus_connection, 1); -    PA_REFCNT_INIT(pconn); -    pconn->core = c; -    pconn->property_name = name; -    pconn->connection = conn; -    pconn->dispatch_event = c->mainloop->defer_new(c->mainloop, dispatch_cb, conn); +    pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER); -    pa_shared_set(c, name, pconn); +    if (!(conn = dbus_bus_get_private(type, error))) +        return NULL; -    return pconn; -} +    pconn = pa_xnew(pa_dbus_wrap_connection, 1); +    pconn->mainloop = m; +    pconn->connection = conn; -DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c){ -    pa_assert(c); -    pa_assert(PA_REFCNT_VALUE(c) > 0); -    pa_assert(c->connection); +    dbus_connection_set_exit_on_disconnect(conn, FALSE); +    dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL); +    dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL); +    dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL); +    dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL); + +    pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn); -    return c->connection; +    return pconn;  } -void pa_dbus_connection_unref(pa_dbus_connection *c) { +void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {      pa_assert(c); -    pa_assert(PA_REFCNT_VALUE(c) > 0); - -    if (PA_REFCNT_DEC(c) > 0) -        return;      if (dbus_connection_get_is_connected(c->connection)) {          dbus_connection_close(c->connection); -         /* must process remaining messages, bit of a kludge to handle +        /* must process remaining messages, bit of a kludge to handle           * both unload and shutdown */          while (dbus_connection_read_write_dispatch(c->connection, -1));      } -    /* already disconnected, just free */ -    pa_shared_remove(c->core, c->property_name); -    c->core->mainloop->defer_free(c->dispatch_event); +    c->mainloop->defer_free(c->dispatch_event);      dbus_connection_unref(c->connection);      pa_xfree(c);  } -pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) { -    pa_assert(c); -    pa_assert(PA_REFCNT_VALUE(c) > 0); +DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *c) { +  pa_assert(c); +  pa_assert(c->connection); -    PA_REFCNT_INC(c); - -    return c; -} - -pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error) { - -    static const char *const prop_name[] = { -        [DBUS_BUS_SESSION] = "dbus-connection-session", -        [DBUS_BUS_SYSTEM] = "dbus-connection-system", -        [DBUS_BUS_STARTER] = "dbus-connection-starter" -    }; -    DBusConnection *conn; -    pa_dbus_connection *pconn; - -    pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER); - -    if ((pconn = pa_shared_get(c, prop_name[type]))) -        return pa_dbus_connection_ref(pconn); - -    if (!(conn = dbus_bus_get_private(type, error))) -        return NULL; - -    pconn = pa_dbus_connection_new(c, conn, prop_name[type]); - -    dbus_connection_set_exit_on_disconnect(conn, FALSE); -    dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL); -    dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, c, NULL); -    dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, c, NULL); -    dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL); - -    return pconn; +  return c->connection;  }  int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) { diff --git a/src/modules/dbus-util.h b/src/pulsecore/dbus-util.h index 554f41a4..55cda7a0 100644 --- a/src/modules/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -24,27 +24,22 @@  #include <dbus/dbus.h> -#include <pulsecore/core.h>  #include <pulsecore/llist.h> +#include <pulse/mainloop-api.h> -typedef struct pa_dbus_connection pa_dbus_connection; +/* A wrap connection is not shared or refcounted, it is available in client side */ +typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection; -/* return the DBusConnection of the specified type for the given core, - * like dbus_bus_get(), but integrates the connection with the pa_core */ -pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error); +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, DBusBusType type, DBusError *error); +void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn); -DBusConnection* pa_dbus_connection_get(pa_dbus_connection *conn); - -pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *conn); -void pa_dbus_connection_unref(pa_dbus_connection *conn); +DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn);  int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) PA_GCC_SENTINEL;  void pa_dbus_remove_matches(DBusConnection *c,  ...) PA_GCC_SENTINEL;  typedef struct pa_dbus_pending pa_dbus_pending; -struct userdata; /* We leave the actual definition to the caller */ -  struct pa_dbus_pending {      DBusConnection *connection;      DBusMessage *message; diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c index e2be42b3..e6e7b736 100644 --- a/src/pulsecore/memblockq.c +++ b/src/pulsecore/memblockq.c @@ -851,6 +851,26 @@ void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {      bq->maxrewind = (maxrewind/bq->base)*bq->base;  } +void pa_memblockq_apply_attr(pa_memblockq *bq, const pa_buffer_attr *a) { +    pa_assert(bq); +    pa_assert(a); + +    pa_memblockq_set_maxlength(bq, a->maxlength); +    pa_memblockq_set_tlength(bq, a->tlength); +    pa_memblockq_set_prebuf(bq, a->prebuf); +    pa_memblockq_set_minreq(bq, a->minreq); +} + +void pa_memblockq_get_attr(pa_memblockq *bq, pa_buffer_attr *a) { +    pa_assert(bq); +    pa_assert(a); + +    a->maxlength = (uint32_t) pa_memblockq_get_maxlength(bq); +    a->tlength = (uint32_t) pa_memblockq_get_tlength(bq); +    a->prebuf = (uint32_t) pa_memblockq_get_prebuf(bq); +    a->minreq = (uint32_t) pa_memblockq_get_minreq(bq); +} +  int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {      pa_assert(bq); diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h index 0a74aa37..e315b831 100644 --- a/src/pulsecore/memblockq.h +++ b/src/pulsecore/memblockq.h @@ -158,6 +158,10 @@ void pa_memblockq_set_minreq(pa_memblockq *memblockq, size_t minreq);  void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t maxrewind); /* Set the maximum history size */  void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memchunk *silence); +/* Apply the data from pa_buffer_attr */ +void pa_memblockq_apply_attr(pa_memblockq *memblockq, const pa_buffer_attr *a); +void pa_memblockq_get_attr(pa_memblockq *bq, pa_buffer_attr *a); +  /* Call pa_memchunk_willneed() for every chunk in the queue from the current read pointer to the end */  void pa_memblockq_willneed(pa_memblockq *bq); @@ -175,5 +179,4 @@ pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq);  /* Return how many items are currently stored in the queue */  unsigned pa_memblockq_get_nblocks(pa_memblockq *bq); -  #endif diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index 6951e10a..d4d7f3ee 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -152,7 +152,7 @@ enum {      /* Supported since protocol v14 (0.9.12) */      PA_COMMAND_EXTENSION, -    /* Supported since protocol v15 (0.9.15*/ +    /* Supported since protocol v15 (0.9.15) */      PA_COMMAND_GET_CARD_INFO,      PA_COMMAND_GET_CARD_INFO_LIST,      PA_COMMAND_SET_CARD_PROFILE, @@ -161,6 +161,10 @@ enum {      PA_COMMAND_PLAYBACK_STREAM_EVENT,      PA_COMMAND_RECORD_STREAM_EVENT, +    /* SERVER->CLIENT */ +    PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED, +    PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED, +      PA_COMMAND_MAX  }; diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index ff49e696..3e75cab8 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -81,7 +81,11 @@ typedef struct record_stream {      pa_source_output *source_output;      pa_memblockq *memblockq; -    size_t fragment_size; + +    pa_bool_t adjust_latency:1; +    pa_bool_t early_requests:1; + +    pa_buffer_attr buffer_attr;      pa_usec_t source_latency;  } record_stream; @@ -105,14 +109,18 @@ typedef struct playback_stream {      pa_sink_input *sink_input;      pa_memblockq *memblockq; + +    pa_bool_t adjust_latency:1; +    pa_bool_t early_requests:1; +      pa_bool_t is_underrun:1;      pa_bool_t drain_request:1;      uint32_t drain_tag;      uint32_t syncid;      pa_atomic_t missing; -    size_t minreq;      pa_usec_t sink_latency; +    pa_buffer_attr buffer_attr;      /* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */      int64_t read_index, write_index; @@ -180,7 +188,8 @@ enum {      SINK_INPUT_MESSAGE_TRIGGER,      SINK_INPUT_MESSAGE_SEEK,      SINK_INPUT_MESSAGE_PREBUF_FORCE, -    SINK_INPUT_MESSAGE_UPDATE_LATENCY +    SINK_INPUT_MESSAGE_UPDATE_LATENCY, +    SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR  };  enum { @@ -188,7 +197,8 @@ enum {      PLAYBACK_STREAM_MESSAGE_UNDERFLOW,      PLAYBACK_STREAM_MESSAGE_OVERFLOW,      PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, -    PLAYBACK_STREAM_MESSAGE_STARTED +    PLAYBACK_STREAM_MESSAGE_STARTED, +    PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH  };  enum { @@ -203,7 +213,7 @@ enum {  static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);  static void sink_input_kill_cb(pa_sink_input *i);  static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend); -static void sink_input_moved_cb(pa_sink_input *i); +static void sink_input_moving_cb(pa_sink_input *i);  static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);  static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);  static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes); @@ -215,7 +225,7 @@ static void playback_stream_request_bytes(struct playback_stream*s);  static void source_output_kill_cb(pa_source_output *o);  static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);  static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend); -static void source_output_moved_cb(pa_source_output *o); +static void source_output_moving_cb(pa_source_output *o);  static pa_usec_t source_output_get_latency_cb(pa_source_output *o);  static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl); @@ -360,6 +370,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {  /* structure management */ +/* Called from main context */  static void upload_stream_unlink(upload_stream *s) {      pa_assert(s); @@ -371,6 +382,7 @@ static void upload_stream_unlink(upload_stream *s) {      upload_stream_unref(s);  } +/* Called from main context */  static void upload_stream_free(pa_object *o) {      upload_stream *s = UPLOAD_STREAM(o);      pa_assert(s); @@ -388,6 +400,7 @@ static void upload_stream_free(pa_object *o) {      pa_xfree(s);  } +/* Called from main context */  static upload_stream* upload_stream_new(          pa_native_connection *c,          const pa_sample_spec *ss, @@ -420,6 +433,7 @@ static upload_stream* upload_stream_new(      return s;  } +/* Called from main context */  static void record_stream_unlink(record_stream *s) {      pa_assert(s); @@ -437,6 +451,7 @@ static void record_stream_unlink(record_stream *s) {      record_stream_unref(s);  } +/* Called from main context */  static void record_stream_free(pa_object *o) {      record_stream *s = RECORD_STREAM(o);      pa_assert(s); @@ -447,6 +462,7 @@ static void record_stream_free(pa_object *o) {      pa_xfree(s);  } +/* Called from main context */  static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {      record_stream *s = RECORD_STREAM(o);      record_stream_assert_ref(s); @@ -472,35 +488,34 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i      return 0;  } -static void fix_record_buffer_attr_pre( -        record_stream *s, -        pa_bool_t adjust_latency, -        pa_bool_t early_requests, -        uint32_t *maxlength, -        uint32_t *fragsize) { +/* Called from main context */ +static void fix_record_buffer_attr_pre(record_stream *s) {      size_t frame_size;      pa_usec_t orig_fragsize_usec, fragsize_usec, source_usec;      pa_assert(s); -    pa_assert(maxlength); -    pa_assert(fragsize); + +    /* This function will be called from the main thread, before as +     * well as after the source output has been activated using +     * pa_source_output_put()! That means it may not touch any +     * ->thread_info data! */      frame_size = pa_frame_size(&s->source_output->sample_spec); -    if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH) -        *maxlength = MAX_MEMBLOCKQ_LENGTH; -    if (*maxlength <= 0) -        *maxlength = (uint32_t) frame_size; +    if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH) +        s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH; +    if (s->buffer_attr.maxlength <= 0) +        s->buffer_attr.maxlength = (uint32_t) frame_size; -    if (*fragsize == (uint32_t) -1) -        *fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec); -    if (*fragsize <= 0) -        *fragsize = (uint32_t) frame_size; +    if (s->buffer_attr.fragsize == (uint32_t) -1) +        s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec); +    if (s->buffer_attr.fragsize <= 0) +        s->buffer_attr.fragsize = (uint32_t) frame_size; -    orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec); +    orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(s->buffer_attr.fragsize, &s->source_output->sample_spec); -    if (early_requests) { +    if (s->early_requests) {          /* In early request mode we need to emulate the classic           * fragment-based playback model. We do this setting the source @@ -508,7 +523,7 @@ static void fix_record_buffer_attr_pre(          source_usec = fragsize_usec; -    } else if (adjust_latency) { +    } else if (s->adjust_latency) {          /* So, the user asked us to adjust the latency according to           * what the source can provide. Half the latency will be @@ -530,14 +545,14 @@ static void fix_record_buffer_attr_pre(      else          s->source_latency = 0; -    if (early_requests) { +    if (s->early_requests) {          /* Ok, we didn't necessarily get what we were asking for, so           * let's tell the user */          fragsize_usec = s->source_latency; -    } else if (adjust_latency) { +    } else if (s->adjust_latency) {          /* Now subtract what we actually got */ @@ -550,45 +565,41 @@ static void fix_record_buffer_attr_pre(      if (pa_usec_to_bytes(orig_fragsize_usec, &s->source_output->sample_spec) !=          pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec)) -        *fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec); +        s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec); -    if (*fragsize <= 0) -        *fragsize = (uint32_t) frame_size; +    if (s->buffer_attr.fragsize <= 0) +        s->buffer_attr.fragsize = (uint32_t) frame_size;  } -static void fix_record_buffer_attr_post( -        record_stream *s, -        uint32_t *maxlength, -        uint32_t *fragsize) { - +/* Called from main context */ +static void fix_record_buffer_attr_post(record_stream *s) {      size_t base;      pa_assert(s); -    pa_assert(maxlength); -    pa_assert(fragsize); -    *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq); +    /* This function will be called from the main thread, before as +     * well as after the source output has been activated using +     * pa_source_output_put()! That means it may not touch and +     * ->thread_info data! */      base = pa_frame_size(&s->source_output->sample_spec); -    s->fragment_size = (*fragsize/base)*base; -    if (s->fragment_size <= 0) -        s->fragment_size = base; +    s->buffer_attr.fragsize = (s->buffer_attr.fragsize/base)*base; +    if (s->buffer_attr.fragsize <= 0) +        s->buffer_attr.fragsize = base; -    if (s->fragment_size > *maxlength) -        s->fragment_size = *maxlength; - -    *fragsize = (uint32_t) s->fragment_size; +    if (s->buffer_attr.fragsize > s->buffer_attr.maxlength) +        s->buffer_attr.fragsize = s->buffer_attr.maxlength;  } +/* Called from main context */  static record_stream* record_stream_new(          pa_native_connection *c,          pa_source *source,          pa_sample_spec *ss,          pa_channel_map *map,          pa_bool_t peak_detect, -        uint32_t *maxlength, -        uint32_t *fragsize, +        pa_buffer_attr *attr,          pa_source_output_flags_t flags,          pa_proplist *p,          pa_bool_t adjust_latency, @@ -603,7 +614,6 @@ static record_stream* record_stream_new(      pa_assert(c);      pa_assert(ss); -    pa_assert(maxlength);      pa_assert(p);      pa_assert(ret); @@ -632,20 +642,23 @@ static record_stream* record_stream_new(      s->parent.process_msg = record_stream_process_msg;      s->connection = c;      s->source_output = source_output; +    s->buffer_attr = *attr; +    s->adjust_latency = adjust_latency; +    s->early_requests = early_requests;      s->source_output->push = source_output_push_cb;      s->source_output->kill = source_output_kill_cb;      s->source_output->get_latency = source_output_get_latency_cb; -    s->source_output->moved = source_output_moved_cb; +    s->source_output->moving = source_output_moving_cb;      s->source_output->suspend = source_output_suspend_cb;      s->source_output->send_event = source_output_send_event_cb;      s->source_output->userdata = s; -    fix_record_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, fragsize); +    fix_record_buffer_attr_pre(s);      s->memblockq = pa_memblockq_new(              0, -            *maxlength, +            s->buffer_attr.maxlength,              0,              base = pa_frame_size(&source_output->sample_spec),              1, @@ -653,7 +666,8 @@ static record_stream* record_stream_new(              0,              NULL); -    fix_record_buffer_attr_post(s, maxlength, fragsize); +    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr); +    fix_record_buffer_attr_post(s);      *ss = s->source_output->sample_spec;      *map = s->source_output->channel_map; @@ -661,14 +675,15 @@ static record_stream* record_stream_new(      pa_idxset_put(c->record_streams, s, &s->index);      pa_log_info("Final latency %0.2f ms = %0.2f ms + %0.2f ms", -                ((double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) + (double) s->source_latency) / PA_USEC_PER_MSEC, -                (double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) / PA_USEC_PER_MSEC, +                ((double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) + (double) s->source_latency) / PA_USEC_PER_MSEC, +                (double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) / PA_USEC_PER_MSEC,                  (double) s->source_latency / PA_USEC_PER_MSEC);      pa_source_output_put(s->source_output);      return s;  } +/* Called from main context */  static void record_stream_send_killed(record_stream *r) {      pa_tagstruct *t;      record_stream_assert_ref(r); @@ -680,6 +695,7 @@ static void record_stream_send_killed(record_stream *r) {      pa_pstream_send_tagstruct(r->connection->pstream, t);  } +/* Called from main context */  static void playback_stream_unlink(playback_stream *s) {      pa_assert(s); @@ -700,6 +716,7 @@ static void playback_stream_unlink(playback_stream *s) {      playback_stream_unref(s);  } +/* Called from main context */  static void playback_stream_free(pa_object* o) {      playback_stream *s = PLAYBACK_STREAM(o);      pa_assert(s); @@ -710,6 +727,7 @@ static void playback_stream_free(pa_object* o) {      pa_xfree(s);  } +/* Called from main context */  static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {      playback_stream *s = PLAYBACK_STREAM(o);      playback_stream_assert_ref(s); @@ -788,67 +806,79 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,          case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:              pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));              break; + +        case PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH: { +            pa_tagstruct *t; + +            s->buffer_attr.tlength = (uint32_t) offset; + +            t = pa_tagstruct_new(NULL, 0); +            pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED); +            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ +            pa_tagstruct_putu32(t, s->index); +            pa_tagstruct_putu32(t, s->buffer_attr.maxlength); +            pa_tagstruct_putu32(t, s->buffer_attr.tlength); +            pa_tagstruct_putu32(t, s->buffer_attr.prebuf); +            pa_tagstruct_putu32(t, s->buffer_attr.minreq); +            pa_tagstruct_put_usec(t, s->sink_latency); +            pa_pstream_send_tagstruct(s->connection->pstream, t); + +            break; +        }      }      return 0;  } -static void fix_playback_buffer_attr_pre( -        playback_stream *s, -        pa_bool_t adjust_latency, -        pa_bool_t early_requests, -        uint32_t *maxlength, -        uint32_t *tlength, -        uint32_t* prebuf, -        uint32_t* minreq) { - +/* Called from main context */ +static void fix_playback_buffer_attr(playback_stream *s) {      size_t frame_size;      pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec;      pa_assert(s); -    pa_assert(maxlength); -    pa_assert(tlength); -    pa_assert(prebuf); -    pa_assert(minreq); + +    /* This function will be called from the main thread, before as +     * well as after the sink input has been activated using +     * pa_sink_input_put()! That means it may not touch any +     * ->thread_info data, such as the memblockq! */      frame_size = pa_frame_size(&s->sink_input->sample_spec); -    if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH) -        *maxlength = MAX_MEMBLOCKQ_LENGTH; -    if (*maxlength <= 0) -        *maxlength = (uint32_t) frame_size; +    if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH) +        s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH; +    if (s->buffer_attr.maxlength <= 0) +        s->buffer_attr.maxlength = (uint32_t) frame_size; -    if (*tlength == (uint32_t) -1) -        *tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec); -    if (*tlength <= 0) -        *tlength = (uint32_t) frame_size; +    if (s->buffer_attr.tlength == (uint32_t) -1) +        s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec); +    if (s->buffer_attr.tlength <= 0) +        s->buffer_attr.tlength = (uint32_t) frame_size; -    if (*minreq == (uint32_t) -1) -        *minreq = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec); -    if (*minreq <= 0) -        *minreq = (uint32_t) frame_size; +    if (s->buffer_attr.minreq == (uint32_t) -1) +        s->buffer_attr.minreq = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec); +    if (s->buffer_attr.minreq <= 0) +        s->buffer_attr.minreq = (uint32_t) frame_size; -    if (*tlength < *minreq+frame_size) -        *tlength = *minreq+(uint32_t) frame_size; +    if (s->buffer_attr.tlength < s->buffer_attr.minreq+frame_size) +        s->buffer_attr.tlength = s->buffer_attr.minreq+(uint32_t) frame_size; -    orig_tlength_usec = tlength_usec = pa_bytes_to_usec(*tlength, &s->sink_input->sample_spec); -    orig_minreq_usec = minreq_usec = pa_bytes_to_usec(*minreq, &s->sink_input->sample_spec); +    orig_tlength_usec = tlength_usec = pa_bytes_to_usec(s->buffer_attr.tlength, &s->sink_input->sample_spec); +    orig_minreq_usec = minreq_usec = pa_bytes_to_usec(s->buffer_attr.minreq, &s->sink_input->sample_spec);      pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms",                  (double) tlength_usec / PA_USEC_PER_MSEC,                  (double) minreq_usec / PA_USEC_PER_MSEC); -    if (early_requests) { +    if (s->early_requests) {          /* In early request mode we need to emulate the classic           * fragment-based playback model. We do this setting the sink           * latency to the fragment size. */          sink_usec = minreq_usec; -          pa_log_debug("Early requests mode enabled, configuring sink latency to minreq."); -    } else if (adjust_latency) { +    } else if (s->adjust_latency) {          /* So, the user asked us to adjust the latency of the stream           * buffer according to the what the sink can provide. The @@ -890,14 +920,14 @@ static void fix_playback_buffer_attr_pre(      s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec); -    if (early_requests) { +    if (s->early_requests) {          /* Ok, we didn't necessarily get what we were asking for, so           * let's tell the user */          minreq_usec = s->sink_latency; -    } else if (adjust_latency) { +    } else if (s->adjust_latency) {          /* Ok, we didn't necessarily get what we were asking for, so           * let's subtract from what we asked for for the remaining @@ -914,54 +944,31 @@ static void fix_playback_buffer_attr_pre(      if (pa_usec_to_bytes_round_up(orig_tlength_usec, &s->sink_input->sample_spec) !=          pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec)) -        *tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec); +        s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec);      if (pa_usec_to_bytes(orig_minreq_usec, &s->sink_input->sample_spec) !=          pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec)) -        *minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec); +        s->buffer_attr.minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec); -    if (*minreq <= 0) { -        *minreq = (uint32_t) frame_size; -        *tlength += (uint32_t) frame_size*2; +    if (s->buffer_attr.minreq <= 0) { +        s->buffer_attr.minreq = (uint32_t) frame_size; +        s->buffer_attr.tlength += (uint32_t) frame_size*2;      } -    if (*tlength <= *minreq) -        *tlength = *minreq*2 + (uint32_t) frame_size; - -    if (*prebuf == (uint32_t) -1 || *prebuf > *tlength) -        *prebuf = *tlength; -} - -static void fix_playback_buffer_attr_post( -        playback_stream *s, -        uint32_t *maxlength, -        uint32_t *tlength, -        uint32_t* prebuf, -        uint32_t* minreq) { - -    pa_assert(s); -    pa_assert(maxlength); -    pa_assert(tlength); -    pa_assert(prebuf); -    pa_assert(minreq); - -    *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq); -    *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq); -    *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq); -    *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq); +    if (s->buffer_attr.tlength <= s->buffer_attr.minreq) +        s->buffer_attr.tlength = s->buffer_attr.minreq*2 + (uint32_t) frame_size; -    s->minreq = *minreq; +    if (s->buffer_attr.prebuf == (uint32_t) -1 || s->buffer_attr.prebuf > s->buffer_attr.tlength) +        s->buffer_attr.prebuf = s->buffer_attr.tlength;  } +/* Called from main context */  static playback_stream* playback_stream_new(          pa_native_connection *c,          pa_sink *sink,          pa_sample_spec *ss,          pa_channel_map *map, -        uint32_t *maxlength, -        uint32_t *tlength, -        uint32_t *prebuf, -        uint32_t *minreq, +        pa_buffer_attr *a,          pa_cvolume *volume,          pa_bool_t muted,          pa_bool_t muted_set, @@ -982,10 +989,6 @@ static playback_stream* playback_stream_new(      pa_assert(c);      pa_assert(ss); -    pa_assert(maxlength); -    pa_assert(tlength); -    pa_assert(prebuf); -    pa_assert(minreq);      pa_assert(missing);      pa_assert(p);      pa_assert(ret); @@ -1042,6 +1045,9 @@ static playback_stream* playback_stream_new(      s->is_underrun = TRUE;      s->drain_request = FALSE;      pa_atomic_store(&s->missing, 0); +    s->buffer_attr = *a; +    s->adjust_latency = adjust_latency; +    s->early_requests = early_requests;      s->sink_input->parent.process_msg = sink_input_process_msg;      s->sink_input->pop = sink_input_pop_cb; @@ -1049,28 +1055,28 @@ static playback_stream* playback_stream_new(      s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;      s->sink_input->update_max_request = sink_input_update_max_request_cb;      s->sink_input->kill = sink_input_kill_cb; -    s->sink_input->moved = sink_input_moved_cb; +    s->sink_input->moving = sink_input_moving_cb;      s->sink_input->suspend = sink_input_suspend_cb;      s->sink_input->send_event = sink_input_send_event_cb;      s->sink_input->userdata = s;      start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0; -    fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, tlength, prebuf, minreq); -    pa_sink_input_get_silence(sink_input, &silence); +    fix_playback_buffer_attr(s); +    pa_sink_input_get_silence(sink_input, &silence);      s->memblockq = pa_memblockq_new(              start_index, -            *maxlength, -            *tlength, +            s->buffer_attr.maxlength, +            s->buffer_attr.tlength,              pa_frame_size(&sink_input->sample_spec), -            *prebuf, -            *minreq, +            s->buffer_attr.prebuf, +            s->buffer_attr.minreq,              0,              &silence); -      pa_memblock_unref(silence.memblock); -    fix_playback_buffer_attr_post(s, maxlength, tlength, prebuf, minreq); + +    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);      *missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq); @@ -1080,18 +1086,18 @@ static playback_stream* playback_stream_new(      pa_idxset_put(c->output_streams, s, &s->index);      pa_log_info("Final latency %0.2f ms = %0.2f ms + 2*%0.2f ms + %0.2f ms", -                ((double) pa_bytes_to_usec(*tlength, &sink_input->sample_spec) + (double) s->sink_latency) / PA_USEC_PER_MSEC, -                (double) pa_bytes_to_usec(*tlength-*minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC, -                (double) pa_bytes_to_usec(*minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC, +                ((double) pa_bytes_to_usec(s->buffer_attr.tlength, &sink_input->sample_spec) + (double) s->sink_latency) / PA_USEC_PER_MSEC, +                (double) pa_bytes_to_usec(s->buffer_attr.tlength-s->buffer_attr.minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC, +                (double) pa_bytes_to_usec(s->buffer_attr.minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,                  (double) s->sink_latency / PA_USEC_PER_MSEC);      pa_sink_input_put(s->sink_input);      return s;  } -/* Called from thread context */ +/* Called from IO context */  static void playback_stream_request_bytes(playback_stream *s) { -    size_t m, previous_missing; +    size_t m, previous_missing, minreq;      playback_stream_assert_ref(s); @@ -1103,13 +1109,15 @@ static void playback_stream_request_bytes(playback_stream *s) {  /*     pa_log("request_bytes(%lu)", (unsigned long) m); */      previous_missing = (size_t) pa_atomic_add(&s->missing, (int) m); +    minreq = pa_memblockq_get_minreq(s->memblockq);      if (pa_memblockq_prebuf_active(s->memblockq) || -        (previous_missing < s->minreq && previous_missing+m >= s->minreq)) +        (previous_missing < minreq && previous_missing+m >= minreq))          pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);  } +/* Called from main context */  static void playback_stream_send_killed(playback_stream *p) {      pa_tagstruct *t;      playback_stream_assert_ref(p); @@ -1121,6 +1129,7 @@ static void playback_stream_send_killed(playback_stream *p) {      pa_pstream_send_tagstruct(p->connection->pstream, t);  } +/* Called from main context */  static int native_connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {      pa_native_connection *c = PA_NATIVE_CONNECTION(o);      pa_native_connection_assert_ref(c); @@ -1142,6 +1151,7 @@ static int native_connection_process_msg(pa_msgobject *o, int code, void*userdat      return 0;  } +/* Called from main context */  static void native_connection_unlink(pa_native_connection *c) {      record_stream *r;      output_stream *o; @@ -1181,6 +1191,7 @@ static void native_connection_unlink(pa_native_connection *c) {      pa_native_connection_unref(c);  } +/* Called from main context */  static void native_connection_free(pa_object *o) {      pa_native_connection *c = PA_NATIVE_CONNECTION(o); @@ -1198,6 +1209,7 @@ static void native_connection_free(pa_object *o) {      pa_xfree(c);  } +/* Called from main context */  static void native_connection_send_memblock(pa_native_connection *c) {      uint32_t start;      record_stream *r; @@ -1217,8 +1229,8 @@ static void native_connection_send_memblock(pa_native_connection *c) {          if (pa_memblockq_peek(r->memblockq,  &chunk) >= 0) {              pa_memchunk schunk = chunk; -            if (schunk.length > r->fragment_size) -                schunk.length = r->fragment_size; +            if (schunk.length > r->buffer_attr.fragsize) +                schunk.length = r->buffer_attr.fragsize;              pa_pstream_send_memblock(c->pstream, r->index, 0, PA_SEEK_RELATIVE, &schunk); @@ -1232,6 +1244,7 @@ static void native_connection_send_memblock(pa_native_connection *c) {  /*** sink input callbacks ***/ +/* Called from thread context */  static void handle_seek(playback_stream *s, int64_t indexw) {      playback_stream_assert_ref(s); @@ -1399,6 +1412,12 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int               * latency added by the resampler */              break;          } + +        case SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR: { +            pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr); +            pa_memblockq_get_attr(s->memblockq, &s->buffer_attr); +            return 0; +        }      }      return pa_sink_input_process_msg(o, code, userdata, offset, chunk); @@ -1447,6 +1466,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk      return 0;  } +/* Called from thread context */  static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {      playback_stream *s; @@ -1461,6 +1481,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {      pa_memblockq_rewind(s->memblockq, nbytes);  } +/* Called from thread context */  static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {      playback_stream *s; @@ -1471,6 +1492,7 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {      pa_memblockq_set_maxrewind(s->memblockq, nbytes);  } +/* Called from thread context */  static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {      playback_stream *s;      size_t tlength; @@ -1481,8 +1503,10 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {      tlength = nbytes+2*pa_memblockq_get_minreq(s->memblockq); -    if (pa_memblockq_get_tlength(s->memblockq) < tlength) +    if (pa_memblockq_get_tlength(s->memblockq) < tlength) {          pa_memblockq_set_tlength(s->memblockq, tlength); +        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH, NULL, pa_memblockq_get_tlength(s->memblockq), NULL, NULL); +    }  }  /* Called from main context */ @@ -1539,26 +1563,17 @@ static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {  }  /* Called from main context */ -static void sink_input_moved_cb(pa_sink_input *i) { +static void sink_input_moving_cb(pa_sink_input *i) {      playback_stream *s;      pa_tagstruct *t; -    uint32_t maxlength, tlength, prebuf, minreq;      pa_sink_input_assert_ref(i);      s = PLAYBACK_STREAM(i->userdata);      playback_stream_assert_ref(s); -    maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq); -    tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq); -    prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq); -    minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq); - -    fix_playback_buffer_attr_pre(s, TRUE, FALSE, &maxlength, &tlength, &prebuf, &minreq); -    pa_memblockq_set_maxlength(s->memblockq, maxlength); -    pa_memblockq_set_tlength(s->memblockq, tlength); -    pa_memblockq_set_prebuf(s->memblockq, prebuf); -    pa_memblockq_set_minreq(s->memblockq, minreq); -    fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq); +    fix_playback_buffer_attr(s); +    pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr); +    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);      if (s->connection->version < 12)        return; @@ -1572,10 +1587,10 @@ static void sink_input_moved_cb(pa_sink_input *i) {      pa_tagstruct_put_boolean(t, pa_sink_get_state(i->sink) == PA_SINK_SUSPENDED);      if (s->connection->version >= 13) { -        pa_tagstruct_putu32(t, maxlength); -        pa_tagstruct_putu32(t, tlength); -        pa_tagstruct_putu32(t, prebuf); -        pa_tagstruct_putu32(t, minreq); +        pa_tagstruct_putu32(t, s->buffer_attr.maxlength); +        pa_tagstruct_putu32(t, s->buffer_attr.tlength); +        pa_tagstruct_putu32(t, s->buffer_attr.prebuf); +        pa_tagstruct_putu32(t, s->buffer_attr.minreq);          pa_tagstruct_put_usec(t, s->sink_latency);      } @@ -1661,21 +1676,18 @@ static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {  }  /* Called from main context */ -static void source_output_moved_cb(pa_source_output *o) { +static void source_output_moving_cb(pa_source_output *o) {      record_stream *s;      pa_tagstruct *t; -    uint32_t maxlength, fragsize;      pa_source_output_assert_ref(o);      s = RECORD_STREAM(o->userdata);      record_stream_assert_ref(s); -    fragsize = (uint32_t) s->fragment_size; -    maxlength = (uint32_t) pa_memblockq_get_length(s->memblockq); - -    fix_record_buffer_attr_pre(s, TRUE, FALSE, &maxlength, &fragsize); -    pa_memblockq_set_maxlength(s->memblockq, maxlength); -    fix_record_buffer_attr_post(s, &maxlength, &fragsize); +    fix_record_buffer_attr_pre(s); +    pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength); +    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr); +    fix_record_buffer_attr_post(s);      if (s->connection->version < 12)        return; @@ -1689,8 +1701,8 @@ static void source_output_moved_cb(pa_source_output *o) {      pa_tagstruct_put_boolean(t, pa_source_get_state(o->source) == PA_SOURCE_SUSPENDED);      if (s->connection->version >= 13) { -        pa_tagstruct_putu32(t, maxlength); -        pa_tagstruct_putu32(t, fragsize); +        pa_tagstruct_putu32(t, s->buffer_attr.maxlength); +        pa_tagstruct_putu32(t, s->buffer_attr.fragsize);          pa_tagstruct_put_usec(t, s->source_latency);      } @@ -1723,7 +1735,8 @@ static pa_tagstruct *reply_new(uint32_t tag) {  static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      playback_stream *s; -    uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing; +    uint32_t sink_index, syncid, missing; +    pa_buffer_attr attr;      const char *name = NULL, *sink_name;      pa_sample_spec ss;      pa_channel_map map; @@ -1752,6 +1765,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u      pa_native_connection_assert_ref(c);      pa_assert(t); +    memset(&attr, 0, sizeof(attr));      if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||          pa_tagstruct_get( @@ -1760,11 +1774,11 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u                  PA_TAG_CHANNEL_MAP, &map,                  PA_TAG_U32, &sink_index,                  PA_TAG_STRING, &sink_name, -                PA_TAG_U32, &maxlength, +                PA_TAG_U32, &attr.maxlength,                  PA_TAG_BOOLEAN, &corked, -                PA_TAG_U32, &tlength, -                PA_TAG_U32, &prebuf, -                PA_TAG_U32, &minreq, +                PA_TAG_U32, &attr.tlength, +                PA_TAG_U32, &attr.prebuf, +                PA_TAG_U32, &attr.minreq,                  PA_TAG_U32, &syncid,                  PA_TAG_CVOLUME, &volume,                  PA_TAG_INVALID) < 0) { @@ -1875,7 +1889,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u       * flag. For older versions we synthesize it here */      muted_set = muted_set || muted; -    s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, &ret); +    s = playback_stream_new(c, sink, &ss, &map, &attr, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, &ret);      pa_proplist_free(p);      CHECK_VALIDITY(c->pstream, s, tag, ret); @@ -1891,10 +1905,10 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u      if (c->version >= 9) {          /* Since 0.9.0 we support sending the buffer metrics back to the client */ -        pa_tagstruct_putu32(reply, (uint32_t) maxlength); -        pa_tagstruct_putu32(reply, (uint32_t) tlength); -        pa_tagstruct_putu32(reply, (uint32_t) prebuf); -        pa_tagstruct_putu32(reply, (uint32_t) minreq); +        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength); +        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.tlength); +        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.prebuf); +        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.minreq);      }      if (c->version >= 12) { @@ -1978,7 +1992,7 @@ static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t t  static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      record_stream *s; -    uint32_t maxlength, fragment_size; +    pa_buffer_attr attr;      uint32_t source_index;      const char *name = NULL, *source_name;      pa_sample_spec ss; @@ -2008,14 +2022,16 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin      pa_native_connection_assert_ref(c);      pa_assert(t); +    memset(&attr, 0, sizeof(attr)); +      if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||          pa_tagstruct_get_sample_spec(t, &ss) < 0 ||          pa_tagstruct_get_channel_map(t, &map) < 0 ||          pa_tagstruct_getu32(t, &source_index) < 0 ||          pa_tagstruct_gets(t, &source_name) < 0 || -        pa_tagstruct_getu32(t, &maxlength) < 0 || +        pa_tagstruct_getu32(t, &attr.maxlength) < 0 ||          pa_tagstruct_get_boolean(t, &corked) < 0 || -        pa_tagstruct_getu32(t, &fragment_size) < 0) { +        pa_tagstruct_getu32(t, &attr.fragsize) < 0) {          protocol_error(c);          return;      } @@ -2125,7 +2141,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin          (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |          (fail_on_suspend ? PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND : 0); -    s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests, &ret); +    s = record_stream_new(c, source, &ss, &map, peak_detect, &attr, flags, p, adjust_latency, direct_on_input, early_requests, &ret);      pa_proplist_free(p);      CHECK_VALIDITY(c->pstream, s, tag, ret); @@ -2138,8 +2154,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin      if (c->version >= 9) {          /* Since 0.9 we support sending the buffer metrics back to the client */ -        pa_tagstruct_putu32(reply, (uint32_t) maxlength); -        pa_tagstruct_putu32(reply, (uint32_t) fragment_size); +        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength); +        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.fragsize);      }      if (c->version >= 12) { @@ -3435,12 +3451,14 @@ static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint  static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      uint32_t idx; -    uint32_t maxlength, tlength, prebuf, minreq, fragsize; +    pa_buffer_attr a;      pa_tagstruct *reply;      pa_native_connection_assert_ref(c);      pa_assert(t); +    memset(&a, 0, sizeof(a)); +      if (pa_tagstruct_getu32(t, &idx) < 0) {          protocol_error(c);          return; @@ -3458,10 +3476,10 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u          if (pa_tagstruct_get(                      t, -                    PA_TAG_U32, &maxlength, -                    PA_TAG_U32, &tlength, -                    PA_TAG_U32, &prebuf, -                    PA_TAG_U32, &minreq, +                    PA_TAG_U32, &a.maxlength, +                    PA_TAG_U32, &a.tlength, +                    PA_TAG_U32, &a.prebuf, +                    PA_TAG_U32, &a.minreq,                      PA_TAG_INVALID) < 0 ||              (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||              (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) || @@ -3470,18 +3488,18 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u              return;          } -        fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &tlength, &prebuf, &minreq); -        pa_memblockq_set_maxlength(s->memblockq, maxlength); -        pa_memblockq_set_tlength(s->memblockq, tlength); -        pa_memblockq_set_prebuf(s->memblockq, prebuf); -        pa_memblockq_set_minreq(s->memblockq, minreq); -        fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq); +        s->adjust_latency = adjust_latency; +        s->early_requests = early_requests; +        s->buffer_attr = a; + +        fix_playback_buffer_attr(s); +        pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR, NULL, 0, NULL) == 0);          reply = reply_new(tag); -        pa_tagstruct_putu32(reply, maxlength); -        pa_tagstruct_putu32(reply, tlength); -        pa_tagstruct_putu32(reply, prebuf); -        pa_tagstruct_putu32(reply, minreq); +        pa_tagstruct_putu32(reply, s->buffer_attr.maxlength); +        pa_tagstruct_putu32(reply, s->buffer_attr.tlength); +        pa_tagstruct_putu32(reply, s->buffer_attr.prebuf); +        pa_tagstruct_putu32(reply, s->buffer_attr.minreq);          if (c->version >= 13)              pa_tagstruct_put_usec(reply, s->sink_latency); @@ -3496,8 +3514,8 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u          if (pa_tagstruct_get(                      t, -                    PA_TAG_U32, &maxlength, -                    PA_TAG_U32, &fragsize, +                    PA_TAG_U32, &a.maxlength, +                    PA_TAG_U32, &a.fragsize,                      PA_TAG_INVALID) < 0 ||              (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||              (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) || @@ -3506,13 +3524,18 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u              return;          } -        fix_record_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &fragsize); -        pa_memblockq_set_maxlength(s->memblockq, maxlength); -        fix_record_buffer_attr_post(s, &maxlength, &fragsize); +        s->adjust_latency = adjust_latency; +        s->early_requests = early_requests; +        s->buffer_attr = a; + +        fix_record_buffer_attr_pre(s); +        pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength); +        pa_memblockq_get_attr(s->memblockq, &s->buffer_attr); +        fix_record_buffer_attr_post(s);          reply = reply_new(tag); -        pa_tagstruct_putu32(reply, maxlength); -        pa_tagstruct_putu32(reply, fragsize); +        pa_tagstruct_putu32(reply, s->buffer_attr.maxlength); +        pa_tagstruct_putu32(reply, s->buffer_attr.fragsize);          if (c->version >= 13)              pa_tagstruct_put_usec(reply, s->source_latency); diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index ae2c6f54..da422428 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -117,7 +117,7 @@ static void reset_callbacks(pa_sink_input *i) {      i->attach = NULL;      i->detach = NULL;      i->suspend = NULL; -    i->moved = NULL; +    i->moving = NULL;      i->kill = NULL;      i->get_latency = NULL;      i->state_change = NULL; @@ -819,26 +819,12 @@ void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes  /* in the  }  /* Called from thread context */ -static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) { -    pa_sink_assert_ref(s); - -    if (usec == (pa_usec_t) -1) -        return usec; - -    if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency) -        usec = s->thread_info.max_latency; - -    if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency) -        usec = s->thread_info.min_latency; - -    return usec; -} - -/* Called from thread context */  pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {      pa_sink_input_assert_ref(i); -    usec = fixup_latency(i->sink, usec); +    if (usec != (pa_usec_t) -1) +        usec =  PA_CLAMP(usec, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); +      i->thread_info.requested_sink_latency = usec;      pa_sink_invalidate_requested_latency(i->sink); @@ -847,33 +833,42 @@ pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa  /* Called from main context */  pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) { +    pa_usec_t min_latency, max_latency; +      pa_sink_input_assert_ref(i); -    if (PA_SINK_INPUT_IS_LINKED(i->state)) +    if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) {          pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); -    else -        /* If this sink input is not realized yet, we have to touch -         * the thread info data directly */ +        return usec; +    } -        i->thread_info.requested_sink_latency = usec; +    /* If this sink input is not realized yet or we are being moved, +     * we have to touch the thread info data directly */ + +    pa_sink_get_latency_range(i->sink, &min_latency, &max_latency); + +    if (usec != (pa_usec_t) -1) +        usec =  PA_CLAMP(usec, min_latency, max_latency); + +    i->thread_info.requested_sink_latency = usec;      return usec;  }  /* Called from main context */  pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) { -    pa_usec_t usec = 0; -      pa_sink_input_assert_ref(i); -    if (PA_SINK_INPUT_IS_LINKED(i->state)) +    if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) { +        pa_usec_t usec = 0;          pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); -    else -        /* If this sink input is not realized yet, we have to touch -         * the thread info data directly */ -        usec = i->thread_info.requested_sink_latency; +        return usec; +    } -    return usec; +    /* If this sink input is not realized yet or we are being moved, +     * we have to touch the thread info data directly */ + +    return i->thread_info.requested_sink_latency;  }  /* Called from main context */ @@ -1169,6 +1164,9 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {      } else          new_resampler = NULL; +    if (i->moving) +        i->moving(i); +      i->sink = dest;      i->save_sink = save;      pa_idxset_put(dest->inputs, i, NULL); @@ -1195,7 +1193,6 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {                  0,                  &i->sink->silence);      } -      pa_sink_update_status(dest);      if (i->sink->flags & PA_SINK_FLAT_VOLUME) { @@ -1216,9 +1213,6 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {      pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name);      /* Notify everyone */ -    if (i->moved) -        i->moved(i); -      pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i);      pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 0bcb9d56..4e29be67 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -150,7 +150,7 @@ struct pa_sink_input {      /* If non-NULL called whenever the the sink this input is attached       * to changes. Called from main context */ -    void (*moved) (pa_sink_input *i);   /* may be NULL */ +    void (*moving) (pa_sink_input *i);   /* may be NULL */      /* Supposed to unlink and destroy this stream. Called from main       * context. */ diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 1fe8f592..73ad247d 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -1798,7 +1798,7 @@ void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind) {      }      if (s->monitor_source) -        pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind); +        pa_source_set_max_rewind_within_thread(s->monitor_source, s->thread_info.max_rewind);  }  /* Called from main thread */ diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 373d5637..550b6571 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -87,7 +87,7 @@ static void reset_callbacks(pa_source_output *o) {      o->attach = NULL;      o->detach = NULL;      o->suspend = NULL; -    o->moved = NULL; +    o->moving = NULL;      o->kill = NULL;      o->get_latency = NULL;      o->state_change = NULL; @@ -516,26 +516,12 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes  /* i  }  /* Called from thread context */ -static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) { -    pa_source_assert_ref(s); - -    if (usec == (pa_usec_t) -1) -        return usec; - -    if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency) -        usec = s->thread_info.max_latency; - -    if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency) -        usec = s->thread_info.min_latency; - -    return usec; -} - -/* Called from thread context */  pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {      pa_source_output_assert_ref(o); -    usec = fixup_latency(o->source, usec); +    if (usec != (pa_usec_t) -1) +        usec = PA_CLAMP(usec, o->source->thread_info.min_latency, o->source->thread_info.max_latency); +      o->thread_info.requested_source_latency = usec;      pa_source_invalidate_requested_latency(o->source); @@ -544,33 +530,42 @@ pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output  /* Called from main context */  pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { +    pa_usec_t min_latency, max_latency; +      pa_source_output_assert_ref(o); -    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) +    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) {          pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); -    else -        /* If this source output is not realized yet, we have to touch -         * the thread info data directly */ +        return usec; +    } -        o->thread_info.requested_source_latency = usec; +    /* If this source output is not realized yet or is being moved, we +     * have to touch the thread info data directly */ + +    pa_source_get_latency_range(o->source, &min_latency, &max_latency); + +    if (usec != (pa_usec_t) -1) +        usec = PA_CLAMP(usec, min_latency, max_latency); + +    o->thread_info.requested_source_latency = usec;      return usec;  }  /* Called from main context */  pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) { -    pa_usec_t usec = 0; -      pa_source_output_assert_ref(o); -    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) +    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) { +        pa_usec_t usec = 0;          pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); -    else -        /* If this source output is not realized yet, we have to touch -         * the thread info data directly */ -        usec = o->thread_info.requested_source_latency; +        return usec; +    } -    return usec; +    /* If this source output is not realized yet or is being moved, we +     * have to touch the thread info data directly */ + +    return o->thread_info.requested_source_latency;  }  /* Called from main context */ @@ -749,6 +744,9 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t      } else          new_resampler = NULL; +    if (o->moving) +        o->moving(o); +      o->source = dest;      o->save_source = save;      pa_idxset_put(o->source->outputs, o, NULL); @@ -776,14 +774,12 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t      }      pa_source_update_status(dest); +      pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);      pa_log_debug("Successfully moved source output %i to %s.", o->index, dest->name);      /* Notify everyone */ -    if (o->moved) -        o->moved(o); -      pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], o);      pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 018ec886..8d57ded4 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -122,7 +122,7 @@ struct pa_source_output {      /* If non-NULL called whenever the the source this output is attached       * to changes. Called from main context */ -    void (*moved) (pa_source_output *o);   /* may be NULL */ +    void (*moving) (pa_source_output *o);   /* may be NULL */      /* Supposed to unlink and destroy this stream. Called from main       * context. */ diff --git a/src/tests/gtk-test.c b/src/tests/gtk-test.c index f82aca58..6470e484 100644 --- a/src/tests/gtk-test.c +++ b/src/tests/gtk-test.c @@ -29,12 +29,38 @@  #include <pulse/context.h>  #include <pulse/glib-mainloop.h> +pa_context *ctxt; +pa_glib_mainloop *m; + +static void context_state_callback(pa_context *c, void *userdata); + +static void connect(void) { +    int r; + +    ctxt = pa_context_new(pa_glib_mainloop_get_api(m), NULL); +    g_assert(ctxt); + +    r = pa_context_connect(ctxt, NULL, PA_CONTEXT_NOAUTOSPAWN|PA_CONTEXT_NOFAIL, NULL); +    g_assert(r == 0); + +    pa_context_set_state_callback(ctxt, context_state_callback, NULL); +} + +static void context_state_callback(pa_context *c, void *userdata) { +    switch (pa_context_get_state(c)) { +        case PA_CONTEXT_FAILED: +            pa_context_unref(ctxt); +            ctxt = NULL; +            connect(); +            break; +        default: +            break; +    } +} +  int main(int argc, char *argv[]) { -    pa_context *c; -    pa_glib_mainloop *m;      GtkWidget *window; -    int r;      gtk_init(&argc, &argv); @@ -49,15 +75,10 @@ int main(int argc, char *argv[]) {      m = pa_glib_mainloop_new(NULL);      g_assert(m); -    c = pa_context_new(pa_glib_mainloop_get_api(m), NULL); -    g_assert(c); - -    r = pa_context_connect(c, NULL, 0, NULL); -    g_assert(r == 0); - +    connect();      gtk_main(); -    pa_context_unref(c); +    pa_context_unref(ctxt);      pa_glib_mainloop_free(m);      return 0;  | 
