summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac84
-rw-r--r--src/.gitignore1
-rw-r--r--src/Makefile.am36
-rw-r--r--src/daemon/daemon-conf.c2
-rw-r--r--src/daemon/main.c11
-rwxr-xr-xsrc/daemon/start-pulseaudio-x11.in1
-rw-r--r--src/modules/alsa/alsa-sink.c574
-rw-r--r--src/modules/alsa/alsa-source.c532
-rw-r--r--src/modules/alsa/alsa-util.c185
-rw-r--r--src/modules/alsa/alsa-util.h8
-rw-r--r--src/modules/alsa/module-alsa-card.c10
-rw-r--r--src/modules/bluetooth/module-bluetooth-device.c169
-rw-r--r--src/modules/bluetooth/sbc.c18
-rw-r--r--src/modules/bluetooth/sbc.h1
-rw-r--r--src/modules/bluetooth/sbc_primitives.c1
-rw-r--r--src/modules/bluetooth/sbc_primitives.h1
-rw-r--r--src/modules/bluetooth/sbc_primitives_mmx.c1
-rw-r--r--src/modules/bluetooth/sbc_primitives_neon.c1
-rw-r--r--src/modules/dbus-util.c3
-rw-r--r--src/modules/module-card-restore.c6
-rw-r--r--src/modules/module-detect.c2
-rw-r--r--src/modules/module-device-restore.c4
-rw-r--r--src/modules/module-hal-detect.c1
-rw-r--r--src/modules/module-protocol-stub.c18
-rw-r--r--src/modules/module-stream-restore.c6
-rw-r--r--src/modules/module-x11-cork-request.c189
-rw-r--r--src/modules/module-x11-publish.c6
-rw-r--r--src/modules/rtp/module-rtp-recv.c20
-rw-r--r--src/modules/rtp/module-rtp-send.c45
-rw-r--r--src/modules/rtp/sap.c17
-rw-r--r--src/modules/rtp/sdp.c13
-rw-r--r--src/pulse/channelmap.c4
-rw-r--r--src/pulse/context.c22
-rw-r--r--src/pulse/gccmacro.h11
-rw-r--r--src/pulse/introspect.c12
-rw-r--r--src/pulse/introspect.h4
-rw-r--r--src/pulse/proplist.h171
-rw-r--r--src/pulse/scache.c4
-rw-r--r--src/pulse/util.c6
-rw-r--r--src/pulsecore/asyncmsgq.c10
-rw-r--r--src/pulsecore/card.c2
-rw-r--r--src/pulsecore/cli-command.c3
-rw-r--r--src/pulsecore/core-error.c3
-rw-r--r--src/pulsecore/core-util.c35
-rw-r--r--src/pulsecore/core-util.h4
-rw-r--r--src/pulsecore/hashmap.c2
-rw-r--r--src/pulsecore/inet_ntop.c4
-rw-r--r--src/pulsecore/inet_pton.c4
-rw-r--r--src/pulsecore/ipacl.c12
-rw-r--r--src/pulsecore/log.c1
-rw-r--r--src/pulsecore/memblock.c2
-rw-r--r--src/pulsecore/proplist-util.c103
-rw-r--r--src/pulsecore/protocol-esound.c2
-rw-r--r--src/pulsecore/protocol-native.c62
-rw-r--r--src/pulsecore/pstream.c9
-rw-r--r--src/pulsecore/rtclock.c8
-rw-r--r--src/pulsecore/rtclock.h2
-rw-r--r--src/pulsecore/rtpoll.c51
-rw-r--r--src/pulsecore/shm.c4
-rw-r--r--src/pulsecore/sink.c3
-rw-r--r--src/pulsecore/socket-client.c18
-rw-r--r--src/pulsecore/socket-client.h2
-rw-r--r--src/pulsecore/socket-server.c10
-rw-r--r--src/pulsecore/socket-server.h8
-rw-r--r--src/pulsecore/socket-util.c8
-rw-r--r--src/pulsecore/source.c3
-rw-r--r--src/tests/alsa-time-test.c200
-rw-r--r--src/tests/gtk-test.c62
-rw-r--r--src/tests/interpol-test.c38
-rw-r--r--src/tests/ipacl-test.c7
-rw-r--r--src/tests/sync-playback.c7
-rw-r--r--src/tests/thread-mainloop-test.c2
-rw-r--r--src/utils/pacat.c3
-rw-r--r--src/utils/pactl.c6
-rw-r--r--src/utils/padsp.c4
-rw-r--r--src/utils/paplay.c5
76 files changed, 2048 insertions, 861 deletions
diff --git a/configure.ac b/configure.ac
index 9189dd0c..11b091bb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -485,7 +485,7 @@ AC_ARG_ENABLE([x11],
[x11=auto])
if test "x${x11}" != xno ; then
- PKG_CHECK_MODULES(X11, [ x11 ice sm ],
+ PKG_CHECK_MODULES(X11, [ x11 ice sm xtst ],
HAVE_X11=1,
[
HAVE_X11=0
@@ -638,7 +638,7 @@ AC_ARG_ENABLE([alsa],
[alsa=auto])
if test "x${alsa}" != xno ; then
- PKG_CHECK_MODULES(ASOUNDLIB, [ alsa >= 1.0.17 ],
+ PKG_CHECK_MODULES(ASOUNDLIB, [ alsa >= 1.0.19 ],
[
HAVE_ALSA=1
AC_DEFINE([HAVE_ALSA], 1, [Have ALSA?])
@@ -646,7 +646,7 @@ if test "x${alsa}" != xno ; then
[
HAVE_ALSA=0
if test "x$alsa" = xyes ; then
- AC_MSG_ERROR([*** Needed alsa >= 1.0.17 support not found])
+ AC_MSG_ERROR([*** Needed alsa >= 1.0.19 support not found])
fi
])
else
@@ -721,6 +721,45 @@ AC_SUBST(GLIB20_LIBS)
AC_SUBST(HAVE_GLIB20)
AM_CONDITIONAL([HAVE_GLIB20], [test "x$HAVE_GLIB20" = x1])
+if test "x$HAVE_GLIB20" = x1 ; then
+ AC_DEFINE([HAVE_GLIB], 1, [Have GLIB?])
+fi
+
+#### GTK2 support (optional) ####
+
+AC_ARG_ENABLE([gtk2],
+ AS_HELP_STRING([--disable-gtk2],[Disable optional Gtk+ 2 support]),
+ [
+ case "${enableval}" in
+ yes) gtk2=yes ;;
+ no) gtk2=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-gtk2) ;;
+ esac
+ ],
+ [gtk2=auto])
+
+if test "x${gtk2}" != xno ; then
+ PKG_CHECK_MODULES(GTK20, [ gtk+-2.0 >= 2.4.0 ],
+ HAVE_GTK20=1,
+ [
+ HAVE_GTK20=0
+ if test "x$gtk2" = xyes ; then
+ AC_MSG_ERROR([*** Gtk+ 2 support not found])
+ fi
+ ])
+else
+ HAVE_GTK20=0
+fi
+
+AC_SUBST(GTK20_CFLAGS)
+AC_SUBST(GTK20_LIBS)
+AC_SUBST(HAVE_GTK20)
+AM_CONDITIONAL([HAVE_GTK20], [test "x$HAVE_GTK20" = x1])
+
+if test "x$HAVE_GTK20" = x1 ; then
+ AC_DEFINE([HAVE_GTK], 1, [Have GTK?])
+fi
+
#### GConf support (optional) ####
AC_ARG_ENABLE([gconf],
@@ -1048,6 +1087,27 @@ AC_SUBST(POLKIT_LIBS)
AC_SUBST(HAVE_POLKIT)
AM_CONDITIONAL([HAVE_POLKIT], [test "x$HAVE_POLKIT" = x1])
+
+### IPv6 connection support (optional) ###
+
+AC_ARG_ENABLE([ipv6],
+ AS_HELP_STRING([--disable-ipv6],[Disable optional IPv6 support]),
+ [
+ case "${enableval}" in
+ yes) ipv6=yes ;;
+ no) ipv6=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-ipv6) ;;
+ esac
+ ],
+ [ipv6=auto])
+
+if test "x${ipv6}" != xno ; then
+ AC_DEFINE([HAVE_IPV6], [1], [Define this to enable IPv6 connection support])
+ HAVE_IPV6=1
+else
+ HAVE_IPV6=0
+fi
+
#### OpenSSL support (optional) ####
AC_ARG_ENABLE([openssl],
@@ -1160,6 +1220,12 @@ AC_SUBST(PA_SYSTEM_STATE_PATH)
# Output #
###################################
+AC_ARG_ENABLE([legacy-runtime-dir],
+ AS_HELP_STRING([--disable-legacy-runtime-dir], [Try to connect on legacy (< 0.9.12) socket paths.]))
+if test "x$enable_legacy_runtime_dir" != "xno" ; then
+ AC_DEFINE(ENABLE_LEGACY_RUNTIME_DIR, [1], [Legacy runtime dir])
+fi
+
AC_ARG_ENABLE(
[static-bins],
AS_HELP_STRING([--enable-static-bins],[Statically link executables.]),
@@ -1229,6 +1295,11 @@ if test "x$HAVE_SOLARIS" = "x1" ; then
ENABLE_SOLARIS=yes
fi
+ENABLE_GTK20=no
+if test "x$HAVE_GTK20" = "x1" ; then
+ ENABLE_GTK20=yes
+fi
+
ENABLE_GLIB20=no
if test "x$HAVE_GLIB20" = "x1" ; then
ENABLE_GLIB20=yes
@@ -1289,6 +1360,11 @@ if test "x${HAVE_OPENSSL}" = "x1" ; then
ENABLE_OPENSSL=yes
fi
+ENABLE_IPV6=no
+if test "x${HAVE_IPV6}" = "x1" ; then
+ ENABLE_IPV6=yes
+fi
+
ENABLE_PER_USER_ESOUND_SOCKET=no
if test "x$per_user_esound_socket" = "x1" ; then
ENABLE_PER_USER_ESOUND_SOCKET=yes
@@ -1311,6 +1387,7 @@ echo "
Enable Alsa: ${ENABLE_ALSA}
Enable Solaris: ${ENABLE_SOLARIS}
Enable GLib 2.0: ${ENABLE_GLIB20}
+ Enable Gtk+ 2.0: ${ENABLE_GTK20}
Enable GConf: ${ENABLE_GCONF}
Enable Avahi: ${ENABLE_AVAHI}
Enable Jack: ${ENABLE_JACK}
@@ -1321,6 +1398,7 @@ echo "
Enable TCP Wrappers: ${ENABLE_TCPWRAP}
Enable libsamplerate: ${ENABLE_LIBSAMPLERATE}
Enable PolicyKit: ${ENABLE_POLKIT}
+ Enable IPv6: ${ENABLE_IPV6}
Enable OpenSSL (for Airtunes): ${ENABLE_OPENSSL}
System User: ${PA_SYSTEM_USER}
diff --git a/src/.gitignore b/src/.gitignore
index 66738d0a..4da445ba 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,3 +1,4 @@
+gtk-test
prioq-test
lock-autospawn-test
*.lo
diff --git a/src/Makefile.am b/src/Makefile.am
index fc478037..ceafdf79 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -308,6 +308,16 @@ TESTS_BINARIES += \
mainloop-test-glib
endif
+if HAVE_GTK20
+TESTS_BINARIES += \
+ gtk-test
+endif
+
+if HAVE_ALSA
+TESTS_BINARIES += \
+ alsa-time-test
+endif
+
if BUILD_TESTS_DEFAULT
noinst_PROGRAMS = $(TESTS_BINARIES)
else
@@ -504,6 +514,16 @@ prioq_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecomm
prioq_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
prioq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+gtk_test_SOURCES = tests/gtk-test.c
+gtk_test_LDADD = $(AM_LDADD) libpulse.la libpulse-mainloop-glib.la
+gtk_test_CFLAGS = $(AM_CFLAGS) $(GTK20_CFLAGS)
+gtk_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(GTK20_LIBS)
+
+alsa_time_test_SOURCES = tests/alsa-time-test.c
+alsa_time_test_LDADD = $(AM_LDADD)
+alsa_time_test_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+alsa_time_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(ASOUNDLIB_LIBS)
+
###################################
# Common library #
###################################
@@ -571,6 +591,9 @@ libpulsecommon_@PA_MAJORMINORMICRO@_la_CFLAGS = $(AM_CFLAGS)
libpulsecommon_@PA_MAJORMINORMICRO@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
libpulsecommon_@PA_MAJORMINORMICRO@_la_LIBADD = $(AM_LIBADD) $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV)
+# proplist-util.h uses these header files, but not the library itself!
+libpulsecommon_@PA_MAJORMINORMICRO@_la_CFLAGS += $(GLIB20_CFLAGS) $(GTK20_CFLAGS)
+
## Please note that libpulsecommon implicitly also depends on<
## libpulse! i.e. we have a cyclic dependancy here. Which is intended
## since libpulse only includes stable, official APIs, while
@@ -937,7 +960,8 @@ if HAVE_X11
modlibexec_LTLIBRARIES += \
module-x11-bell.la \
module-x11-publish.la \
- module-x11-xsmp.la
+ module-x11-xsmp.la \
+ module-x11-cork-request.la
endif
if HAVE_OSS
@@ -946,6 +970,8 @@ modlibexec_LTLIBRARIES += \
module-oss.la
endif
+pulselibexec_PROGRAMS =
+
if HAVE_ALSA
modlibexec_LTLIBRARIES += \
libalsa-util.la \
@@ -981,8 +1007,6 @@ modlibexec_LTLIBRARIES += \
module-jack-source.la
endif
-pulselibexec_PROGRAMS =
-
if HAVE_GCONF
modlibexec_LTLIBRARIES += \
module-gconf.la
@@ -1059,6 +1083,7 @@ SYMDEF_FILES = \
modules/module-x11-bell-symdef.h \
modules/module-x11-publish-symdef.h \
modules/module-x11-xsmp-symdef.h \
+ modules/module-x11-cork-request-symdef.h \
modules/oss/module-oss-symdef.h \
modules/alsa/module-alsa-sink-symdef.h \
modules/alsa/module-alsa-source-symdef.h \
@@ -1247,6 +1272,11 @@ module_x11_xsmp_la_CFLAGS = $(AM_CFLAGS) $(X11_CFLAGS)
module_x11_xsmp_la_LDFLAGS = $(MODULE_LDFLAGS)
module_x11_xsmp_la_LIBADD = $(AM_LIBADD) $(X11_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+module_x11_cork_request_la_SOURCES = modules/module-x11-cork-request.c
+module_x11_cork_request_la_CFLAGS = $(AM_CFLAGS) $(X11_CFLAGS)
+module_x11_cork_request_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_x11_cork_request_la_LIBADD = $(AM_LIBADD) $(X11_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+
# OSS
liboss_util_la_SOURCES = modules/oss/oss-util.c modules/oss/oss-util.h
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index 7d3b89f0..7dfef27f 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -639,7 +639,7 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
if (c->config_file)
pa_strbuf_printf(s, _("### Read from configuration file: %s ###\n"), c->config_file);
- pa_assert(c->log_level <= PA_LOG_LEVEL_MAX);
+ pa_assert(c->log_level < PA_LOG_LEVEL_MAX);
pa_strbuf_printf(s, "daemonize = %s\n", pa_yes_no(c->daemonize));
pa_strbuf_printf(s, "fail = %s\n", pa_yes_no(c->fail));
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 936c214d..5f94ec66 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -348,7 +348,6 @@ int main(int argc, char *argv[]) {
pa_time_event *win32_timer;
struct timeval win32_tv;
#endif
- char *lf = NULL;
int autospawn_fd = -1;
pa_bool_t autospawn_locked = FALSE;
@@ -369,8 +368,11 @@ int main(int argc, char *argv[]) {
* value of $LD_BIND_NOW on initialization. */
pa_set_env("LD_BIND_NOW", "1");
- pa_assert_se(rp = pa_readlink("/proc/self/exe"));
- pa_assert_se(execv(rp, argv) == 0);
+
+ if ((rp = pa_readlink("/proc/self/exe")))
+ pa_assert_se(execv(rp, argv) == 0);
+ else
+ pa_log_warn("Couldn't read /proc/self/exe, cannot self execute. Running in a chroot()?");
}
#endif
@@ -997,9 +999,6 @@ finish:
pa_autospawn_lock_done(FALSE);
}
- if (lf)
- pa_xfree(lf);
-
#ifdef OS_IS_WIN32
if (win32_timer)
pa_mainloop_get_api(mainloop)->time_free(win32_timer);
diff --git a/src/daemon/start-pulseaudio-x11.in b/src/daemon/start-pulseaudio-x11.in
index 3cccc4dc..391a6d3c 100755
--- a/src/daemon/start-pulseaudio-x11.in
+++ b/src/daemon/start-pulseaudio-x11.in
@@ -24,6 +24,7 @@ set -e
if [ x"$DISPLAY" != x ] ; then
@PACTL_BINARY@ load-module module-x11-publish "display=$DISPLAY" > /dev/null
+ @PACTL_BINARY@ load-module module-x11-cork-request "display=$DISPLAY" > /dev/null
if [ x"$SESSION_MANAGER" != x ] ; then
@PACTL_BINARY@ load-module module-x11-xsmp "display=$DISPLAY session_manager=$SESSION_MANAGER" > /dev/null
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index c56614c8..239a9d78 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -35,6 +35,7 @@
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulse/timeval.h>
+#include <pulse/i18n.h>
#include <pulsecore/core.h>
#include <pulsecore/module.h>
@@ -55,11 +56,13 @@
#include "alsa-util.h"
#include "alsa-sink.h"
+/* #define DEBUG_TIMING */
+
#define DEFAULT_DEVICE "default"
#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */
#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */
-#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */
-#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */
+#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */
+#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */
struct userdata {
pa_core *core;
@@ -77,17 +80,17 @@ struct userdata {
snd_mixer_elem_t *mixer_elem;
long hw_volume_max, hw_volume_min;
long hw_dB_max, hw_dB_min;
- pa_bool_t hw_dB_supported;
- pa_bool_t mixer_seperate_channels;
+ pa_bool_t hw_dB_supported:1;
+ pa_bool_t mixer_seperate_channels:1;
pa_cvolume hardware_volume;
- size_t frame_size, fragment_size, hwbuf_size, tsched_watermark;
+ size_t frame_size, fragment_size, hwbuf_size, tsched_watermark, hwbuf_unused, min_sleep, min_wakeup;
unsigned nfragments;
pa_memchunk memchunk;
char *device_name;
- pa_bool_t use_mmap, use_tsched;
+ pa_bool_t use_mmap:1, use_tsched:1;
pa_bool_t first, after_rewind;
@@ -96,39 +99,70 @@ struct userdata {
snd_mixer_selem_channel_id_t mixer_map[SND_MIXER_SCHN_LAST];
pa_smoother *smoother;
- int64_t frame_index;
+ uint64_t write_count;
uint64_t since_start;
-
- snd_pcm_sframes_t hwbuf_unused_frames;
};
static void userdata_free(struct userdata *u);
+static void fix_min_sleep_wakeup(struct userdata *u) {
+ size_t max_use, max_use_2;
+
+ pa_assert(u);
+
+ max_use = u->hwbuf_size - u->hwbuf_unused;
+ max_use_2 = pa_frame_align(max_use/2, &u->sink->sample_spec);
+
+ u->min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->sink->sample_spec);
+ u->min_sleep = PA_CLAMP(u->min_sleep, u->frame_size, max_use_2);
+
+ u->min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->sink->sample_spec);
+ u->min_wakeup = PA_CLAMP(u->min_wakeup, u->frame_size, max_use_2);
+}
+
static void fix_tsched_watermark(struct userdata *u) {
size_t max_use;
- size_t min_sleep, min_wakeup;
pa_assert(u);
- max_use = u->hwbuf_size - (size_t) u->hwbuf_unused_frames * u->frame_size;
+ max_use = u->hwbuf_size - u->hwbuf_unused;
+
+ if (u->tsched_watermark > max_use - u->min_sleep)
+ u->tsched_watermark = max_use - u->min_sleep;
+
+ if (u->tsched_watermark < u->min_wakeup)
+ u->tsched_watermark = u->min_wakeup;
+}
+
+static void adjust_after_underrun(struct userdata *u) {
+ size_t old_watermark;
+ pa_usec_t old_min_latency, new_min_latency;
+
+ pa_assert(u);
+
+ /* First, just try to increase the watermark */
+ old_watermark = u->tsched_watermark;
+ u->tsched_watermark *= 2;
+ fix_tsched_watermark(u);
- min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->sink->sample_spec);
- min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->sink->sample_spec);
+ if (old_watermark != u->tsched_watermark) {
+ pa_log_notice("Increasing wakeup watermark to %0.2f ms",
+ (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
+ return;
+ }
- if (min_sleep > max_use/2)
- min_sleep = pa_frame_align(max_use/2, &u->sink->sample_spec);
- if (min_sleep < u->frame_size)
- min_sleep = u->frame_size;
+ /* Hmm, we cannot increase the watermark any further, hence let's raise the latency */
+ old_min_latency = u->sink->thread_info.min_latency;
+ new_min_latency = PA_MIN(old_min_latency * 2, u->sink->thread_info.max_latency);
- if (min_wakeup > max_use/2)
- min_wakeup = pa_frame_align(max_use/2, &u->sink->sample_spec);
- if (min_wakeup < u->frame_size)
- min_wakeup = u->frame_size;
+ if (old_min_latency != new_min_latency) {
+ pa_log_notice("Increasing minimal latency to %0.2f ms",
+ (double) new_min_latency / PA_USEC_PER_MSEC);
- if (u->tsched_watermark > max_use-min_sleep)
- u->tsched_watermark = max_use-min_sleep;
+ pa_sink_update_latency_range(u->sink, new_min_latency, u->sink->thread_info.max_latency);
+ return;
+ }
- if (u->tsched_watermark < min_wakeup)
- u->tsched_watermark = min_wakeup;
+ /* When we reach this we're officialy fucked! */
}
static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
@@ -144,17 +178,20 @@ static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*p
if (usec == (pa_usec_t) -1)
usec = pa_bytes_to_usec(u->hwbuf_size, &u->sink->sample_spec);
-/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */
-
wm = pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec);
- if (usec >= wm) {
- *sleep_usec = usec - wm;
- *process_usec = wm;
- } else
- *process_usec = *sleep_usec = usec / 2;
+ if (wm > usec)
+ wm = usec/2;
+
+ *sleep_usec = usec - wm;
+ *process_usec = wm;
-/* pa_log_debug("after watermark: %u ms", (unsigned) (*sleep_usec / PA_USEC_PER_MSEC)); */
+#ifdef DEBUG_TIMING
+ pa_log_debug("Buffer time: %lu ms; Sleep time: %lu ms; Process time: %lu ms",
+ (unsigned long) (usec / PA_USEC_PER_MSEC),
+ (unsigned long) (*sleep_usec / PA_USEC_PER_MSEC),
+ (unsigned long) (*process_usec / PA_USEC_PER_MSEC));
+#endif
}
static int try_recover(struct userdata *u, const char *call, int err) {
@@ -169,40 +206,45 @@ static int try_recover(struct userdata *u, const char *call, int err) {
if (err == -EPIPE)
pa_log_debug("%s: Buffer underrun!", call);
- if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) {
- u->first = TRUE;
- u->since_start = 0;
- return 0;
+ if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) < 0) {
+ pa_log("%s: %s", call, snd_strerror(err));
+ return -1;
}
- pa_log("%s: %s", call, snd_strerror(err));
- return -1;
+ u->first = TRUE;
+ u->since_start = 0;
+ return 0;
}
-static size_t check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) {
+static size_t check_left_to_play(struct userdata *u, size_t n_bytes) {
size_t left_to_play;
- if ((size_t) n*u->frame_size < u->hwbuf_size)
- left_to_play = u->hwbuf_size - ((size_t) n*u->frame_size);
- else
- left_to_play = 0;
+ /* We use <= instead of < for this check here because an underrun
+ * only happens after the last sample was processed, not already when
+ * it is removed from the buffer. This is particularly important
+ * when block transfer is used. */
- if (left_to_play > 0) {
-/* pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); */
- } else if (!u->first && !u->after_rewind) {
+ if (n_bytes <= u->hwbuf_size) {
+ left_to_play = u->hwbuf_size - n_bytes;
- if (pa_log_ratelimit())
- pa_log_info("Underrun!");
+#ifdef DEBUG_TIMING
+ pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
+#endif
- if (u->use_tsched) {
- size_t old_watermark = u->tsched_watermark;
+ } else {
+ left_to_play = 0;
- u->tsched_watermark *= 2;
- fix_tsched_watermark(u);
+#ifdef DEBUG_TIMING
+ PA_DEBUG_TRAP;
+#endif
+
+ if (!u->first && !u->after_rewind) {
- if (old_watermark != u->tsched_watermark)
- pa_log_notice("Increasing wakeup watermark to %0.2f ms",
- (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
+ if (pa_log_ratelimit())
+ pa_log_info("Underrun!");
+
+ if (u->use_tsched)
+ adjust_after_underrun(u);
}
}
@@ -210,7 +252,7 @@ static size_t check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) {
}
static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
- int work_done = 0;
+ pa_bool_t work_done = TRUE;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_play;
@@ -222,22 +264,27 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
for (;;) {
snd_pcm_sframes_t n;
+ size_t n_bytes;
int r;
- snd_pcm_hwsync(u->pcm_handle);
-
/* First we determine how many samples are missing to fill the
* buffer up to 100% */
- if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
+ if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
- if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
+ if ((r = try_recover(u, "snd_pcm_avail", (int) n)) == 0)
continue;
return r;
}
- left_to_play = check_left_to_play(u, n);
+ n_bytes = (size_t) n * u->frame_size;
+
+#ifdef DEBUG_TIMING
+ pa_log_debug("avail: %lu", (unsigned long) n_bytes);
+#endif
+
+ left_to_play = check_left_to_play(u, n_bytes);
if (u->use_tsched)
@@ -248,33 +295,42 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
* a single hw buffer length. */
if (!polled &&
- pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
+ pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2) {
+#ifdef DEBUG_TIMING
+ pa_log_debug("Not filling up, because too early.");
+#endif
break;
+ }
- if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) {
+ if (PA_UNLIKELY(n_bytes <= u->hwbuf_unused)) {
if (polled && pa_log_ratelimit())
- pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
- "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
- "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0.");
+ pa_log(_("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
+ "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+ "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0."));
+#ifdef DEBUG_TIMING
+ pa_log_debug("Not filling up, because not necessary.");
+#endif
break;
}
- n -= u->hwbuf_unused_frames;
-
+ n_bytes -= u->hwbuf_unused;
polled = FALSE;
-/* pa_log_debug("Filling up"); */
+#ifdef DEBUG_TIMING
+ pa_log_debug("Filling up");
+#endif
for (;;) {
pa_memchunk chunk;
void *p;
int err;
const snd_pcm_channel_area_t *areas;
- snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n;
+ snd_pcm_uframes_t offset, frames;
snd_pcm_sframes_t sframes;
+ frames = (snd_pcm_uframes_t) (n_bytes / u->frame_size);
/* pa_log_debug("%lu frames to write", (unsigned long) frames); */
if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
@@ -304,9 +360,6 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
chunk.index = 0;
pa_sink_render_into_full(u->sink, &chunk);
-
- /* FIXME: Maybe we can do something to keep this memory block
- * a little bit longer around? */
pa_memblock_unref_fixed(chunk.memblock);
if (PA_UNLIKELY((sframes = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
@@ -317,26 +370,28 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
return r;
}
- work_done = 1;
+ work_done = TRUE;
- u->frame_index += (int64_t) frames;
+ u->write_count += frames * u->frame_size;
u->since_start += frames * u->frame_size;
-/* pa_log_debug("wrote %lu frames", (unsigned long) frames); */
+#ifdef DEBUG_TIMING
+ pa_log_debug("Wrote %lu bytes", (unsigned long) (frames * u->frame_size));
+#endif
- if (frames >= (snd_pcm_uframes_t) n)
+ if ((size_t) frames * u->frame_size >= n_bytes)
break;
- n -= (snd_pcm_sframes_t) frames;
+ n_bytes -= (size_t) frames * u->frame_size;
}
}
*sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) - process_usec;
- return work_done;
+ return work_done ? 1 : 0;
}
static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
- int work_done = 0;
+ pa_bool_t work_done = FALSE;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_play;
@@ -348,19 +403,19 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
for (;;) {
snd_pcm_sframes_t n;
+ size_t n_bytes;
int r;
- snd_pcm_hwsync(u->pcm_handle);
-
- if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
+ if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
- if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
+ if ((r = try_recover(u, "snd_pcm_avail", (int) n)) == 0)
continue;
return r;
}
- left_to_play = check_left_to_play(u, n);
+ n_bytes = (size_t) n * u->frame_size;
+ left_to_play = check_left_to_play(u, n_bytes);
if (u->use_tsched)
@@ -374,18 +429,17 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
break;
- if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) {
+ if (PA_UNLIKELY(n_bytes <= u->hwbuf_unused)) {
if (polled && pa_log_ratelimit())
- pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
- "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
- "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0.");
+ pa_log(_("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
+ "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+ "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0."));
break;
}
- n -= u->hwbuf_unused_frames;
-
+ n_bytes -= u->hwbuf_unused;
polled = FALSE;
for (;;) {
@@ -395,14 +449,14 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
/* pa_log_debug("%lu frames to write", (unsigned long) frames); */
if (u->memchunk.length <= 0)
- pa_sink_render(u->sink, (size_t) n * u->frame_size, &u->memchunk);
+ pa_sink_render(u->sink, n_bytes, &u->memchunk);
pa_assert(u->memchunk.length > 0);
frames = (snd_pcm_sframes_t) (u->memchunk.length / u->frame_size);
- if (frames > n)
- frames = n;
+ if (frames > (snd_pcm_sframes_t) (n_bytes/u->frame_size))
+ frames = (snd_pcm_sframes_t) (n_bytes/u->frame_size);
p = pa_memblock_acquire(u->memchunk.memblock);
frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, (snd_pcm_uframes_t) frames);
@@ -426,30 +480,29 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
pa_memchunk_reset(&u->memchunk);
}
- work_done = 1;
+ work_done = TRUE;
- u->frame_index += frames;
- u->since_start += (size_t) frames * u->frame_size;
+ u->write_count += frames * u->frame_size;
+ u->since_start += frames * u->frame_size;
/* pa_log_debug("wrote %lu frames", (unsigned long) frames); */
- if (frames >= n)
+ if ((size_t) frames * u->frame_size >= n_bytes)
break;
- n -= frames;
+ n_bytes -= (size_t) frames * u->frame_size;
}
}
*sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) - process_usec;
- return work_done;
+ return work_done ? 1 : 0;
}
static void update_smoother(struct userdata *u) {
- snd_pcm_sframes_t delay = 0;
- int64_t frames;
+ snd_pcm_sframes_t delay = 0;
+ int64_t position;
int err;
- pa_usec_t now1, now2;
-/* struct timeval timestamp; */
+ pa_usec_t now1 = 0, now2;
snd_pcm_status_t *status;
snd_pcm_status_alloca(&status);
@@ -459,36 +512,35 @@ static void update_smoother(struct userdata *u) {
/* Let's update the time smoother */
- snd_pcm_hwsync(u->pcm_handle);
- snd_pcm_avail_update(u->pcm_handle);
-
-/* if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { */
-/* pa_log("Failed to query DSP status data: %s", snd_strerror(err)); */
-/* return; */
-/* } */
-
-/* delay = snd_pcm_status_get_delay(status); */
-
if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) {
- pa_log("Failed to query DSP status data: %s", snd_strerror(err));
+ pa_log_warn("Failed to query DSP status data: %s", snd_strerror(err));
return;
}
- frames = u->frame_index - delay;
+ if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0))
+ pa_log_warn("Failed to get timestamp: %s", snd_strerror(err));
+ else {
+ snd_htimestamp_t htstamp = { 0, 0 };
+ snd_pcm_status_get_htstamp(status, &htstamp);
+ now1 = pa_timespec_load(&htstamp);
+ }
-/* pa_log_debug("frame_index = %llu, delay = %llu, p = %llu", (unsigned long long) u->frame_index, (unsigned long long) delay, (unsigned long long) frames); */
+ position = (int64_t) u->write_count - ((int64_t) delay * (int64_t) u->frame_size);
-/* snd_pcm_status_get_tstamp(status, &timestamp); */
-/* pa_rtclock_from_wallclock(&timestamp); */
-/* now1 = pa_timeval_load(&timestamp); */
+ if (PA_UNLIKELY(position < 0))
+ position = 0;
+
+ /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */
+ if (now1 <= 0)
+ now1 = pa_rtclock_usec();
+
+ now2 = pa_bytes_to_usec((uint64_t) position, &u->sink->sample_spec);
- now1 = pa_rtclock_usec();
- now2 = pa_bytes_to_usec((uint64_t) frames * u->frame_size, &u->sink->sample_spec);
pa_smoother_put(u->smoother, now1, now2);
}
static pa_usec_t sink_get_latency(struct userdata *u) {
- pa_usec_t r = 0;
+ pa_usec_t r;
int64_t delay;
pa_usec_t now1, now2;
@@ -497,10 +549,9 @@ static pa_usec_t sink_get_latency(struct userdata *u) {
now1 = pa_rtclock_usec();
now2 = pa_smoother_get(u->smoother, now1);
- delay = (int64_t) pa_bytes_to_usec((uint64_t) u->frame_index * u->frame_size, &u->sink->sample_spec) - (int64_t) now2;
+ delay = (int64_t) pa_bytes_to_usec(u->write_count, &u->sink->sample_spec) - (int64_t) now2;
- if (delay > 0)
- r = (pa_usec_t) delay;
+ r = delay >= 0 ? (pa_usec_t) delay : 0;
if (u->memchunk.memblock)
r += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
@@ -549,7 +600,7 @@ static int update_sw_params(struct userdata *u) {
pa_assert(u);
/* Use the full buffer if noone asked us for anything specific */
- u->hwbuf_unused_frames = 0;
+ u->hwbuf_unused = 0;
if (u->use_tsched) {
pa_usec_t latency;
@@ -557,7 +608,7 @@ static int update_sw_params(struct userdata *u) {
if ((latency = pa_sink_get_requested_latency_within_thread(u->sink)) != (pa_usec_t) -1) {
size_t b;
- pa_log_debug("latency set to %0.2fms", (double) latency / PA_USEC_PER_MSEC);
+ pa_log_debug("Latency set to %0.2fms", (double) latency / PA_USEC_PER_MSEC);
b = pa_usec_to_bytes(latency, &u->sink->sample_spec);
@@ -566,18 +617,17 @@ static int update_sw_params(struct userdata *u) {
if (PA_UNLIKELY(b < u->frame_size))
b = u->frame_size;
- u->hwbuf_unused_frames = (snd_pcm_sframes_t)
- (PA_LIKELY(b < u->hwbuf_size) ?
- ((u->hwbuf_size - b) / u->frame_size) : 0);
+ u->hwbuf_unused = PA_LIKELY(b < u->hwbuf_size) ? (u->hwbuf_size - b) : 0;
}
+ fix_min_sleep_wakeup(u);
fix_tsched_watermark(u);
}
- pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames);
+ pa_log_debug("hwbuf_unused=%lu", (unsigned long) u->hwbuf_unused);
/* We need at last one frame in the used part of the buffer */
- avail_min = (snd_pcm_uframes_t) u->hwbuf_unused_frames + 1;
+ avail_min = (snd_pcm_uframes_t) u->hwbuf_unused / u->frame_size + 1;
if (u->use_tsched) {
pa_usec_t sleep_usec, process_usec;
@@ -593,7 +643,7 @@ static int update_sw_params(struct userdata *u) {
return err;
}
- pa_sink_set_max_request(u->sink, u->hwbuf_size - (size_t) u->hwbuf_unused_frames * u->frame_size);
+ pa_sink_set_max_request(u->sink, u->hwbuf_size - u->hwbuf_unused);
return 0;
}
@@ -654,8 +704,6 @@ static int unsuspend(struct userdata *u) {
if (build_pollfd(u) < 0)
goto fail;
- /* FIXME: We need to reload the volume somehow */
-
u->first = TRUE;
u->since_start = 0;
@@ -980,13 +1028,13 @@ static void sink_set_mute_cb(pa_sink *s) {
static void sink_update_requested_latency_cb(pa_sink *s) {
struct userdata *u = s->userdata;
- snd_pcm_sframes_t before;
+ size_t before;
pa_assert(u);
if (!u->pcm_handle)
return;
- before = u->hwbuf_unused_frames;
+ before = u->hwbuf_unused;
update_sw_params(u);
/* Let's check whether we now use only a smaller part of the
@@ -995,7 +1043,7 @@ static void sink_update_requested_latency_cb(pa_sink *s) {
current fill level. Thus, let's do a full rewind once, to clear
things up. */
- if (u->hwbuf_unused_frames > before) {
+ if (u->hwbuf_unused > before) {
pa_log_debug("Requesting rewind due to latency change.");
pa_sink_request_rewind(s, (size_t) -1);
}
@@ -1050,7 +1098,7 @@ static int process_rewind(struct userdata *u) {
if (rewind_nbytes <= 0)
pa_log_info("Tried rewind, but was apparently not possible.");
else {
- u->frame_index -= out_frames;
+ u->write_count -= out_frames * u->frame_size;
pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
pa_sink_process_rewind(u->sink, rewind_nbytes);
@@ -1085,7 +1133,9 @@ static void thread_func(void *userdata) {
for (;;) {
int ret;
-/* pa_log_debug("loop"); */
+#ifdef DEBUG_TIMING
+ pa_log_debug("Loop");
+#endif
/* Render some data and write it to the dsp */
if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
@@ -1131,7 +1181,7 @@ static void thread_func(void *userdata) {
* we have filled the buffer at least once
* completely.*/
- /*pa_log_debug("Cutting sleep time for the initial iterations by half.");*/
+ pa_log_debug("Cutting sleep time for the initial iterations by half.");
sleep_usec /= 2;
}
@@ -1177,16 +1227,15 @@ static void thread_func(void *userdata) {
goto fail;
}
- if (revents & (POLLIN|POLLERR|POLLNVAL|POLLHUP|POLLPRI)) {
+ if (revents & ~POLLOUT) {
if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
goto fail;
u->first = TRUE;
u->since_start = 0;
- }
+ } else if (revents && u->use_tsched && pa_log_ratelimit())
+ pa_log_debug("Wakeup from ALSA!");
- if (revents && u->use_tsched && pa_log_ratelimit())
- pa_log_debug("Wakeup from ALSA!%s%s", (revents & POLLIN) ? " INPUT" : "", (revents & POLLOUT) ? " OUTPUT" : "");
} else
revents = 0;
}
@@ -1227,6 +1276,91 @@ static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *de
pa_xfree(t);
}
+static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
+ pa_assert(u);
+
+ if (!u->mixer_handle)
+ return 0;
+
+ pa_assert(u->mixer_elem);
+
+ if (snd_mixer_selem_has_playback_volume(u->mixer_elem)) {
+ pa_bool_t suitable = FALSE;
+
+ if (snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) < 0)
+ pa_log_info("Failed to get volume range. Falling back to software volume control.");
+ else if (u->hw_volume_min >= u->hw_volume_max)
+ pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", u->hw_volume_min, u->hw_volume_max);
+ else {
+ pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max);
+ suitable = TRUE;
+ }
+
+ if (suitable) {
+ if (ignore_dB || snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
+ pa_log_info("Mixer doesn't support dB information or data is ignored.");
+ else {
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min));
+ VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_max, sizeof(u->hw_dB_max));
+#endif
+
+ if (u->hw_dB_min >= u->hw_dB_max)
+ pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0);
+ else {
+ pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0);
+ u->hw_dB_supported = TRUE;
+
+ if (u->hw_dB_max > 0) {
+ u->sink->base_volume = pa_sw_volume_from_dB(- (double) u->hw_dB_max/100.0);
+ pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->sink->base_volume));
+ } else
+ pa_log_info("No particular base volume set, fixing to 0 dB");
+ }
+ }
+
+ if (!u->hw_dB_supported &&
+ u->hw_volume_max - u->hw_volume_min < 3) {
+
+ pa_log_info("Device doesn't do dB volume and has less than 4 volume levels. Falling back to software volume control.");
+ suitable = FALSE;
+ }
+ }
+
+ if (suitable) {
+ u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &u->sink->channel_map, u->mixer_map, TRUE) >= 0;
+
+ u->sink->get_volume = sink_get_volume_cb;
+ u->sink->set_volume = sink_set_volume_cb;
+ u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0);
+ pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
+
+ if (!u->hw_dB_supported)
+ u->sink->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1;
+ } else
+ pa_log_info("Using software volume control.");
+ }
+
+ if (snd_mixer_selem_has_playback_switch(u->mixer_elem)) {
+ u->sink->get_mute = sink_get_mute_cb;
+ u->sink->set_mute = sink_set_mute_cb;
+ u->sink->flags |= PA_SINK_HW_MUTE_CTRL;
+ } else
+ pa_log_info("Using software mute control.");
+
+ u->mixer_fdl = pa_alsa_fdlist_new();
+
+ if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, u->core->mainloop) < 0) {
+ pa_log("Failed to initialize file descriptor monitoring");
+ return -1;
+ }
+
+ snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback);
+ snd_mixer_elem_set_callback_private(u->mixer_elem, u);
+
+ return 0;
+}
+
pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile) {
struct userdata *u = NULL;
@@ -1236,14 +1370,10 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
snd_pcm_uframes_t period_frames, tsched_frames;
size_t frame_size;
- snd_pcm_info_t *pcm_info = NULL;
- int err;
pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_usec_t usec;
pa_sink_new_data data;
- snd_pcm_info_alloca(&pcm_info);
-
pa_assert(m);
pa_assert(ma);
@@ -1300,11 +1430,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
u->use_mmap = use_mmap;
u->use_tsched = use_tsched;
u->first = TRUE;
- u->since_start = 0;
- u->after_rewind = FALSE;
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
- u->alsa_rtpoll_item = NULL;
u->smoother = pa_smoother_new(DEFAULT_TSCHED_BUFFER_USEC*2, DEFAULT_TSCHED_BUFFER_USEC*2, TRUE, 5);
usec = pa_rtclock_usec();
@@ -1368,7 +1495,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
}
if (use_tsched && (!b || !d)) {
- pa_log_info("Cannot enabled timer-based scheduling, falling back to sound IRQ scheduling.");
+ pa_log_info("Cannot enable timer-based scheduling, falling back to sound IRQ scheduling.");
u->use_tsched = use_tsched = FALSE;
}
@@ -1378,51 +1505,10 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
if (u->use_tsched)
pa_log_info("Successfully enabled timer-based scheduling mode.");
- if ((err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {
- pa_log("Error fetching PCM info: %s", snd_strerror(err));
- goto fail;
- }
-
/* ALSA might tweak the sample spec, so recalculate the frame size */
frame_size = pa_frame_size(&ss);
- if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0)
- pa_log_warn("Error opening mixer: %s", snd_strerror(err));
- else {
- pa_bool_t found = FALSE;
-
- if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0)
- found = TRUE;
- else {
- snd_pcm_info_t *info;
-
- snd_pcm_info_alloca(&info);
-
- if (snd_pcm_info(u->pcm_handle, info) >= 0) {
- char *md;
- int card_idx;
-
- if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
-
- md = pa_sprintf_malloc("hw:%i", card_idx);
-
- if (strcmp(u->device_name, md))
- if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
- found = TRUE;
- pa_xfree(md);
- }
- }
- }
-
- if (found)
- if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Master", "PCM", TRUE)))
- found = FALSE;
-
- if (!found) {
- snd_mixer_close(u->mixer_handle);
- u->mixer_handle = NULL;
- }
- }
+ pa_alsa_find_mixer_and_elem(u->pcm_handle, &u->mixer_handle, &u->mixer_elem);
pa_sink_new_data_init(&data);
data.driver = driver;
@@ -1432,7 +1518,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
pa_sink_new_data_set_sample_spec(&data, &ss);
pa_sink_new_data_set_channel_map(&data, &map);
- pa_alsa_init_proplist_pcm(m->core, data.proplist, pcm_info);
+ pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
@@ -1462,17 +1548,13 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size);
u->nfragments = nfrags;
u->hwbuf_size = u->fragment_size * nfrags;
- u->hwbuf_unused_frames = 0;
u->tsched_watermark = tsched_watermark;
- u->frame_index = 0;
- u->hw_dB_supported = FALSE;
- u->hw_dB_min = u->hw_dB_max = 0;
- u->hw_volume_min = u->hw_volume_max = 0;
- u->mixer_seperate_channels = FALSE;
pa_cvolume_mute(&u->hardware_volume, u->sink->sample_spec.channels);
- if (use_tsched)
+ if (use_tsched) {
+ fix_min_sleep_wakeup(u);
fix_tsched_watermark(u);
+ }
u->sink->thread_info.max_rewind = use_tsched ? u->hwbuf_size : 0;
u->sink->thread_info.max_request = u->hwbuf_size;
@@ -1492,86 +1574,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
if (update_sw_params(u) < 0)
goto fail;
- pa_memchunk_reset(&u->memchunk);
-
- if (u->mixer_handle) {
- pa_assert(u->mixer_elem);
-
- if (snd_mixer_selem_has_playback_volume(u->mixer_elem)) {
- pa_bool_t suitable = FALSE;
-
- if (snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) < 0)
- pa_log_info("Failed to get volume range. Falling back to software volume control.");
- else if (u->hw_volume_min >= u->hw_volume_max)
- pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", u->hw_volume_min, u->hw_volume_max);
- else {
- pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max);
- suitable = TRUE;
- }
-
- if (suitable) {
- if (ignore_dB || snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
- pa_log_info("Mixer doesn't support dB information or data is ignored.");
- else {
-#ifdef HAVE_VALGRIND_MEMCHECK_H
- VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min));
- VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_max, sizeof(u->hw_dB_max));
-#endif
-
- if (u->hw_dB_min >= u->hw_dB_max)
- pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0);
- else {
- pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0);
- u->hw_dB_supported = TRUE;
-
- if (u->hw_dB_max > 0) {
- u->sink->base_volume = pa_sw_volume_from_dB(- (double) u->hw_dB_max/100.0);
- pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->sink->base_volume));
- } else
- pa_log_info("No particular base volume set, fixing to 0 dB");
- }
- }
-
- if (!u->hw_dB_supported &&
- u->hw_volume_max - u->hw_volume_min < 3) {
-
- pa_log_info("Device doesn't do dB volume and has less than 4 volume levels. Falling back to software volume control.");
- suitable = FALSE;
- }
- }
-
- if (suitable) {
- u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, TRUE) >= 0;
-
- u->sink->get_volume = sink_get_volume_cb;
- u->sink->set_volume = sink_set_volume_cb;
- u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0);
- pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
-
- if (!u->hw_dB_supported)
- u->sink->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1;
- } else
- pa_log_info("Using software volume control.");
- }
-
- if (snd_mixer_selem_has_playback_switch(u->mixer_elem)) {
- u->sink->get_mute = sink_get_mute_cb;
- u->sink->set_mute = sink_set_mute_cb;
- u->sink->flags |= PA_SINK_HW_MUTE_CTRL;
- } else
- pa_log_info("Using software mute control.");
-
- u->mixer_fdl = pa_alsa_fdlist_new();
-
- if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, m->core->mainloop) < 0) {
- pa_log("Failed to initialize file descriptor monitoring");
- goto fail;
- }
-
- snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback);
- snd_mixer_elem_set_callback_private(u->mixer_elem, u);
- } else
- u->mixer_fdl = NULL;
+ if (setup_mixer(u, ignore_dB) < 0)
+ goto fail;
pa_alsa_dump(u->pcm_handle);
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 2b42d3f9..50cdb310 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -35,6 +35,7 @@
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulse/timeval.h>
+#include <pulse/i18n.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core.h>
@@ -56,11 +57,13 @@
#include "alsa-util.h"
#include "alsa-source.h"
+/* #define DEBUG_TIMING */
+
#define DEFAULT_DEVICE "default"
#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */
#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */
-#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */
-#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */
+#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */
+#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */
struct userdata {
pa_core *core;
@@ -78,55 +81,85 @@ struct userdata {
snd_mixer_elem_t *mixer_elem;
long hw_volume_max, hw_volume_min;
long hw_dB_max, hw_dB_min;
- pa_bool_t hw_dB_supported;
- pa_bool_t mixer_seperate_channels;
+ pa_bool_t hw_dB_supported:1;
+ pa_bool_t mixer_seperate_channels:1;
pa_cvolume hardware_volume;
- size_t frame_size, fragment_size, hwbuf_size, tsched_watermark;
+ size_t frame_size, fragment_size, hwbuf_size, tsched_watermark, hwbuf_unused, min_sleep, min_wakeup;
unsigned nfragments;
char *device_name;
- pa_bool_t use_mmap, use_tsched;
+ pa_bool_t use_mmap:1, use_tsched:1;
pa_rtpoll_item *alsa_rtpoll_item;
snd_mixer_selem_channel_id_t mixer_map[SND_MIXER_SCHN_LAST];
pa_smoother *smoother;
- int64_t frame_index;
-
- snd_pcm_sframes_t hwbuf_unused_frames;
+ uint64_t read_count;
};
static void userdata_free(struct userdata *u);
+static void fix_min_sleep_wakeup(struct userdata *u) {
+ size_t max_use, max_use_2;
+ pa_assert(u);
+
+ max_use = u->hwbuf_size - u->hwbuf_unused;
+ max_use_2 = pa_frame_align(max_use/2, &u->source->sample_spec);
+
+ u->min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->source->sample_spec);
+ u->min_sleep = PA_CLAMP(u->min_sleep, u->frame_size, max_use_2);
+
+ u->min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->source->sample_spec);
+ u->min_wakeup = PA_CLAMP(u->min_wakeup, u->frame_size, max_use_2);
+}
+
static void fix_tsched_watermark(struct userdata *u) {
size_t max_use;
- size_t min_sleep, min_wakeup;
pa_assert(u);
- max_use = u->hwbuf_size - (size_t) u->hwbuf_unused_frames * u->frame_size;
+ max_use = u->hwbuf_size - u->hwbuf_unused;
+
+ if (u->tsched_watermark > max_use - u->min_sleep)
+ u->tsched_watermark = max_use - u->min_sleep;
+
+ if (u->tsched_watermark < u->min_wakeup)
+ u->tsched_watermark = u->min_wakeup;
+}
+
+static void adjust_after_overrun(struct userdata *u) {
+ size_t old_watermark;
+ pa_usec_t old_min_latency, new_min_latency;
+
+ pa_assert(u);
+
+ /* First, just try to increase the watermark */
+ old_watermark = u->tsched_watermark;
+ u->tsched_watermark *= 2;
+ fix_tsched_watermark(u);
- min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->source->sample_spec);
- min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->source->sample_spec);
+ if (old_watermark != u->tsched_watermark) {
+ pa_log_notice("Increasing wakeup watermark to %0.2f ms",
+ (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);
+ return;
+ }
- if (min_sleep > max_use/2)
- min_sleep = pa_frame_align(max_use/2, &u->source->sample_spec);
- if (min_sleep < u->frame_size)
- min_sleep = u->frame_size;
+ /* Hmm, we cannot increase the watermark any further, hence let's raise the latency */
+ old_min_latency = u->source->thread_info.min_latency;
+ new_min_latency = PA_MIN(old_min_latency * 2, u->source->thread_info.max_latency);
- if (min_wakeup > max_use/2)
- min_wakeup = pa_frame_align(max_use/2, &u->source->sample_spec);
- if (min_wakeup < u->frame_size)
- min_wakeup = u->frame_size;
+ if (old_min_latency != new_min_latency) {
+ pa_log_notice("Increasing minimal latency to %0.2f ms",
+ (double) new_min_latency / PA_USEC_PER_MSEC);
- if (u->tsched_watermark > max_use-min_sleep)
- u->tsched_watermark = max_use-min_sleep;
+ pa_source_update_latency_range(u->source, new_min_latency, u->source->thread_info.max_latency);
+ return;
+ }
- if (u->tsched_watermark < min_wakeup)
- u->tsched_watermark = min_wakeup;
+ /* When we reach this we're officialy fucked! */
}
static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
@@ -139,17 +172,20 @@ static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_use
if (usec == (pa_usec_t) -1)
usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec);
-/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */
-
wm = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec);
- if (usec >= wm) {
- *sleep_usec = usec - wm;
- *process_usec = wm;
- } else
- *process_usec = *sleep_usec = usec /= 2;
+ if (wm > usec)
+ wm = usec/2;
+
+ *sleep_usec = usec - wm;
+ *process_usec = wm;
-/* pa_log_debug("after watermark: %u ms", (unsigned) (*sleep_usec / PA_USEC_PER_MSEC)); */
+#ifdef DEBUG_TIMING
+ pa_log_debug("Buffer time: %lu ms; Sleep time: %lu ms; Process time: %lu ms",
+ (unsigned long) (usec / PA_USEC_PER_MSEC),
+ (unsigned long) (*sleep_usec / PA_USEC_PER_MSEC),
+ (unsigned long) (*process_usec / PA_USEC_PER_MSEC));
+#endif
return usec;
}
@@ -166,47 +202,50 @@ static int try_recover(struct userdata *u, const char *call, int err) {
if (err == -EPIPE)
pa_log_debug("%s: Buffer overrun!", call);
- if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) {
- snd_pcm_start(u->pcm_handle);
- return 0;
+ if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) < 0) {
+ pa_log("%s: %s", call, snd_strerror(err));
+ return -1;
}
- pa_log("%s: %s", call, snd_strerror(err));
- return -1;
+ snd_pcm_start(u->pcm_handle);
+ return 0;
}
-static size_t check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) {
+static size_t check_left_to_record(struct userdata *u, size_t n_bytes) {
size_t left_to_record;
- size_t rec_space = u->hwbuf_size - (size_t) u->hwbuf_unused_frames*u->frame_size;
+ size_t rec_space = u->hwbuf_size - u->hwbuf_unused;
- if ((size_t) n*u->frame_size < rec_space)
- left_to_record = rec_space - ((size_t) n*u->frame_size);
- else
- left_to_record = 0;
+ /* We use <= instead of < for this check here because an overrun
+ * only happens after the last sample was processed, not already when
+ * it is removed from the buffer. This is particularly important
+ * when block transfer is used. */
+
+ if (n_bytes <= rec_space) {
+ left_to_record = rec_space - n_bytes;
+
+#ifdef DEBUG_TIMING
+ pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC);
+#endif
- if (left_to_record > 0) {
-/* pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); */
} else {
- if (pa_log_ratelimit())
- pa_log_info("Overrun!");
+ left_to_record = 0;
- if (u->use_tsched) {
- size_t old_watermark = u->tsched_watermark;
+#ifdef DEBUG_TIMING
+ PA_DEBUG_TRAP;
+#endif
- u->tsched_watermark *= 2;
- fix_tsched_watermark(u);
+ if (pa_log_ratelimit())
+ pa_log_info("Overrun!");
- if (old_watermark != u->tsched_watermark)
- pa_log_notice("Increasing wakeup watermark to %0.2f ms",
- (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);
- }
+ if (u->use_tsched)
+ adjust_after_overrun(u);
}
return left_to_record;
}
static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
- int work_done = 0;
+ pa_bool_t work_done = FALSE;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_record;
@@ -218,45 +257,63 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
for (;;) {
snd_pcm_sframes_t n;
+ size_t n_bytes;
int r;
- snd_pcm_hwsync(u->pcm_handle);
-
- if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
+ if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
- if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
+ if ((r = try_recover(u, "snd_pcm_avail", (int) n)) == 0)
continue;
return r;
}
- left_to_record = check_left_to_record(u, n);
+ n_bytes = (size_t) n * u->frame_size;
+
+#ifdef DEBUG_TIMING
+ pa_log_debug("avail: %lu", (unsigned long) n_bytes);
+#endif
+
+ left_to_record = check_left_to_record(u, n_bytes);
if (u->use_tsched)
if (!polled &&
- pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
+ pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2) {
+#ifdef DEBUG_TIMING
+ pa_log_debug("Not reading, because too early.");
+#endif
break;
+ }
- if (PA_UNLIKELY(n <= 0)) {
+ if (PA_UNLIKELY(n_bytes <= 0)) {
if (polled && pa_log_ratelimit())
- pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
- "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
- "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0.");
+ pa_log(_("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
+ "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+ "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0."));
+#ifdef DEBUG_TIMING
+ pa_log_debug("Not reading, because not necessary.");
+#endif
break;
}
polled = FALSE;
+#ifdef DEBUG_TIMING
+ pa_log_debug("Reading");
+#endif
+
for (;;) {
int err;
const snd_pcm_channel_area_t *areas;
- snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n;
+ snd_pcm_uframes_t offset, frames;
pa_memchunk chunk;
void *p;
snd_pcm_sframes_t sframes;
+ frames = (snd_pcm_uframes_t) (n_bytes / u->frame_size);
+
/* pa_log_debug("%lu frames to read", (unsigned long) frames); */
if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
@@ -296,25 +353,27 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
return r;
}
- work_done = 1;
+ work_done = TRUE;
- u->frame_index += (int64_t) frames;
+ u->read_count += frames * u->frame_size;
-/* pa_log_debug("read %lu frames", (unsigned long) frames); */
+#ifdef DEBUG_TIMING
+ pa_log_debug("Read %lu bytes", (unsigned long) (frames * u->frame_size));
+#endif
- if (frames >= (snd_pcm_uframes_t) n)
+ if ((size_t) frames * u->frame_size >= n_bytes)
break;
- n -= (snd_pcm_sframes_t) frames;
+ n_bytes -= (size_t) frames * u->frame_size;
}
}
*sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec) - process_usec;
- return work_done;
+ return work_done ? 1 : 0;
}
static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
- int work_done = 0;
+ int work_done = FALSE;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_record;
@@ -326,33 +385,33 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
for (;;) {
snd_pcm_sframes_t n;
+ size_t n_bytes;
int r;
- snd_pcm_hwsync(u->pcm_handle);
-
- if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
+ if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
- if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
+ if ((r = try_recover(u, "snd_pcm_avail", (int) n)) == 0)
continue;
return r;
}
- left_to_record = check_left_to_record(u, n);
+ n_bytes = (size_t) n * u->frame_size;
+ left_to_record = check_left_to_record(u, n_bytes);
if (u->use_tsched)
if (!polled &&
pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
break;
- if (PA_UNLIKELY(n <= 0)) {
+ if (PA_UNLIKELY(n_bytes <= 0)) {
if (polled && pa_log_ratelimit())
- pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
- "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
- "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0.");
+ pa_log(_("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
+ "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+ "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0."));
- return work_done;
+ break;
}
polled = FALSE;
@@ -366,8 +425,8 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
frames = (snd_pcm_sframes_t) (pa_memblock_get_length(chunk.memblock) / u->frame_size);
- if (frames > n)
- frames = n;
+ if (frames > (snd_pcm_sframes_t) (n_bytes/u->frame_size))
+ frames = (snd_pcm_sframes_t) (n_bytes/u->frame_size);
/* pa_log_debug("%lu frames to read", (unsigned long) n); */
@@ -392,53 +451,63 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
pa_source_post(u->source, &chunk);
pa_memblock_unref(chunk.memblock);
- work_done = 1;
+ work_done = TRUE;
- u->frame_index += frames;
+ u->read_count += frames * u->frame_size;
/* pa_log_debug("read %lu frames", (unsigned long) frames); */
- if (frames >= n)
+ if ((size_t) frames * u->frame_size >= n_bytes)
break;
- n -= frames;
+ n_bytes -= (size_t) frames * u->frame_size;
}
}
*sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec) - process_usec;
- return work_done;
+ return work_done ? 1 : 0;
}
static void update_smoother(struct userdata *u) {
snd_pcm_sframes_t delay = 0;
- int64_t frames;
+ uint64_t position;
int err;
- pa_usec_t now1, now2;
+ pa_usec_t now1 = 0, now2;
+ snd_pcm_status_t *status;
+
+ snd_pcm_status_alloca(&status);
pa_assert(u);
pa_assert(u->pcm_handle);
/* Let's update the time smoother */
- snd_pcm_hwsync(u->pcm_handle);
- snd_pcm_avail_update(u->pcm_handle);
-
if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) {
pa_log_warn("Failed to get delay: %s", snd_strerror(err));
return;
}
- frames = u->frame_index + delay;
+ if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0))
+ pa_log_warn("Failed to get timestamp: %s", snd_strerror(err));
+ else {
+ snd_htimestamp_t htstamp = { 0, 0 };
+ snd_pcm_status_get_htstamp(status, &htstamp);
+ now1 = pa_timespec_load(&htstamp);
+ }
- now1 = pa_rtclock_usec();
- now2 = pa_bytes_to_usec((uint64_t) frames * u->frame_size, &u->source->sample_spec);
+ position = u->read_count + ((uint64_t) delay * (uint64_t) u->frame_size);
+
+ /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */
+ if (now1 <= 0)
+ now1 = pa_rtclock_usec();
+
+ now2 = pa_bytes_to_usec(position, &u->source->sample_spec);
pa_smoother_put(u->smoother, now1, now2);
}
static pa_usec_t source_get_latency(struct userdata *u) {
- pa_usec_t r = 0;
- int64_t delay;
+ int64_t delay;
pa_usec_t now1, now2;
pa_assert(u);
@@ -446,12 +515,9 @@ static pa_usec_t source_get_latency(struct userdata *u) {
now1 = pa_rtclock_usec();
now2 = pa_smoother_get(u->smoother, now1);
- delay = (int64_t) now2 - (int64_t) pa_bytes_to_usec((uint64_t) u->frame_index * u->frame_size, &u->source->sample_spec);
-
- if (delay > 0)
- r = (pa_usec_t) delay;
+ delay = (int64_t) now2 - (int64_t) pa_bytes_to_usec(u->read_count, &u->source->sample_spec);
- return r;
+ return delay >= 0 ? (pa_usec_t) delay : 0;
}
static int build_pollfd(struct userdata *u) {
@@ -494,7 +560,7 @@ static int update_sw_params(struct userdata *u) {
pa_assert(u);
/* Use the full buffer if noone asked us for anything specific */
- u->hwbuf_unused_frames = 0;
+ u->hwbuf_unused = 0;
if (u->use_tsched) {
pa_usec_t latency;
@@ -511,15 +577,14 @@ static int update_sw_params(struct userdata *u) {
if (PA_UNLIKELY(b < u->frame_size))
b = u->frame_size;
- u->hwbuf_unused_frames = (snd_pcm_sframes_t)
- (PA_LIKELY(b < u->hwbuf_size) ?
- ((u->hwbuf_size - b) / u->frame_size) : 0);
+ u->hwbuf_unused = PA_LIKELY(b < u->hwbuf_size) ? (u->hwbuf_size - b) : 0;
}
+ fix_min_sleep_wakeup(u);
fix_tsched_watermark(u);
}
- pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames);
+ pa_log_debug("hwbuf_unused=%lu", (unsigned long) u->hwbuf_unused);
avail_min = 1;
@@ -951,11 +1016,13 @@ static void thread_func(void *userdata) {
for (;;) {
int ret;
-/* pa_log_debug("loop"); */
+#ifdef DEBUG_TIMING
+ pa_log_debug("Loop");
+#endif
/* Read some data and pass it to the sources */
if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
- int work_done = 0;
+ int work_done;
pa_usec_t sleep_usec = 0;
if (u->use_mmap)
@@ -1013,15 +1080,14 @@ static void thread_func(void *userdata) {
goto fail;
}
- if (revents & (POLLOUT|POLLERR|POLLNVAL|POLLHUP|POLLPRI)) {
+ if (revents & ~POLLIN) {
if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
goto fail;
snd_pcm_start(u->pcm_handle);
- }
+ } else if (revents && u->use_tsched && pa_log_ratelimit())
+ pa_log_debug("Wakeup from ALSA!");
- if (revents && u->use_tsched && pa_log_ratelimit())
- pa_log_debug("Wakeup from ALSA!%s%s", (revents & POLLIN) ? " INPUT" : "", (revents & POLLOUT) ? " OUTPUT" : "");
} else
revents = 0;
}
@@ -1062,6 +1128,91 @@ static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char
pa_xfree(t);
}
+static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
+ pa_assert(u);
+
+ if (!u->mixer_handle)
+ return 0;
+
+ pa_assert(u->mixer_elem);
+
+ if (snd_mixer_selem_has_capture_volume(u->mixer_elem)) {
+ pa_bool_t suitable = FALSE;
+
+ if (snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) < 0)
+ pa_log_info("Failed to get volume range. Falling back to software volume control.");
+ else if (u->hw_volume_min >= u->hw_volume_max)
+ pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", u->hw_volume_min, u->hw_volume_max);
+ else {
+ pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max);
+ suitable = TRUE;
+ }
+
+ if (suitable) {
+ if (ignore_dB || snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
+ pa_log_info("Mixer doesn't support dB information or data is ignored.");
+ else {
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min));
+ VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_max, sizeof(u->hw_dB_max));
+#endif
+
+ if (u->hw_dB_min >= u->hw_dB_max)
+ pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0);
+ else {
+ pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0);
+ u->hw_dB_supported = TRUE;
+
+ if (u->hw_dB_max > 0) {
+ u->source->base_volume = pa_sw_volume_from_dB(- (double) u->hw_dB_max/100.0);
+ pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->source->base_volume));
+ } else
+ pa_log_info("No particular base volume set, fixing to 0 dB");
+ }
+ }
+
+ if (!u->hw_dB_supported &&
+ u->hw_volume_max - u->hw_volume_min < 3) {
+
+ pa_log_info("Device has less than 4 volume levels. Falling back to software volume control.");
+ suitable = FALSE;
+ }
+ }
+
+ if (suitable) {
+ u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &u->source->channel_map, u->mixer_map, FALSE) >= 0;
+
+ u->source->get_volume = source_get_volume_cb;
+ u->source->set_volume = source_set_volume_cb;
+ u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0);
+ pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
+
+ if (!u->hw_dB_supported)
+ u->source->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1;
+ } else
+ pa_log_info("Using software volume control.");
+ }
+
+ if (snd_mixer_selem_has_capture_switch(u->mixer_elem)) {
+ u->source->get_mute = source_get_mute_cb;
+ u->source->set_mute = source_set_mute_cb;
+ u->source->flags |= PA_SOURCE_HW_MUTE_CTRL;
+ } else
+ pa_log_info("Using software mute control.");
+
+ u->mixer_fdl = pa_alsa_fdlist_new();
+
+ if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, u->core->mainloop) < 0) {
+ pa_log("Failed to initialize file descriptor monitoring");
+ return -1;
+ }
+
+ snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback);
+ snd_mixer_elem_set_callback_private(u->mixer_elem, u);
+
+ return 0;
+}
+
pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile) {
struct userdata *u = NULL;
@@ -1071,14 +1222,11 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
snd_pcm_uframes_t period_frames, tsched_frames;
size_t frame_size;
- snd_pcm_info_t *pcm_info = NULL;
- int err;
pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_source_new_data data;
- snd_pcm_info_alloca(&pcm_info);
-
pa_assert(m);
+ pa_assert(ma);
ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
@@ -1136,7 +1284,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->alsa_rtpoll_item = NULL;
- u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC, DEFAULT_TSCHED_WATERMARK_USEC, TRUE, 5);
+ u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC*2, DEFAULT_TSCHED_WATERMARK_USEC*2, TRUE, 5);
pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
b = use_mmap;
@@ -1193,7 +1341,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
}
if (use_tsched && (!b || !d)) {
- pa_log_info("Cannot enabled timer-based scheduling, falling back to sound IRQ scheduling.");
+ pa_log_info("Cannot enable timer-based scheduling, falling back to sound IRQ scheduling.");
u->use_tsched = use_tsched = FALSE;
}
@@ -1203,51 +1351,10 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
if (u->use_tsched)
pa_log_info("Successfully enabled timer-based scheduling mode.");
- if ((err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {
- pa_log("Error fetching PCM info: %s", snd_strerror(err));
- goto fail;
- }
-
/* ALSA might tweak the sample spec, so recalculate the frame size */
frame_size = pa_frame_size(&ss);
- if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0)
- pa_log("Error opening mixer: %s", snd_strerror(err));
- else {
- pa_bool_t found = FALSE;
-
- if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0)
- found = TRUE;
- else {
- snd_pcm_info_t* info;
-
- snd_pcm_info_alloca(&info);
-
- if (snd_pcm_info(u->pcm_handle, info) >= 0) {
- char *md;
- int card_idx;
-
- if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
-
- md = pa_sprintf_malloc("hw:%i", card_idx);
-
- if (strcmp(u->device_name, md))
- if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
- found = TRUE;
- pa_xfree(md);
- }
- }
- }
-
- if (found)
- if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture", "Mic", FALSE)))
- found = FALSE;
-
- if (!found) {
- snd_mixer_close(u->mixer_handle);
- u->mixer_handle = NULL;
- }
- }
+ pa_alsa_find_mixer_and_elem(u->pcm_handle, &u->mixer_handle, &u->mixer_elem);
pa_source_new_data_init(&data);
data.driver = driver;
@@ -1257,7 +1364,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
pa_source_new_data_set_sample_spec(&data, &ss);
pa_source_new_data_set_channel_map(&data, &map);
- pa_alsa_init_proplist_pcm(m->core, data.proplist, pcm_info);
+ pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
@@ -1287,17 +1394,13 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size);
u->nfragments = nfrags;
u->hwbuf_size = u->fragment_size * nfrags;
- u->hwbuf_unused_frames = 0;
u->tsched_watermark = tsched_watermark;
- u->frame_index = 0;
- u->hw_dB_supported = FALSE;
- u->hw_dB_min = u->hw_dB_max = 0;
- u->hw_volume_min = u->hw_volume_max = 0;
- u->mixer_seperate_channels = FALSE;
pa_cvolume_mute(&u->hardware_volume, u->source->sample_spec.channels);
- if (use_tsched)
+ if (use_tsched) {
+ fix_min_sleep_wakeup(u);
fix_tsched_watermark(u);
+ }
pa_source_set_latency_range(u->source,
!use_tsched ? pa_bytes_to_usec(u->hwbuf_size, &ss) : (pa_usec_t) -1,
@@ -1314,85 +1417,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
if (update_sw_params(u) < 0)
goto fail;
- if (u->mixer_handle) {
- pa_assert(u->mixer_elem);
-
- if (snd_mixer_selem_has_capture_volume(u->mixer_elem)) {
- pa_bool_t suitable = FALSE;
-
- if (snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) < 0)
- pa_log_info("Failed to get volume range. Falling back to software volume control.");
- else if (u->hw_volume_min >= u->hw_volume_max)
- pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", u->hw_volume_min, u->hw_volume_max);
- else {
- pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max);
- suitable = TRUE;
- }
-
- if (suitable) {
- if (ignore_dB || snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
- pa_log_info("Mixer doesn't support dB information or data is ignored.");
- else {
-#ifdef HAVE_VALGRIND_MEMCHECK_H
- VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min));
- VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_max, sizeof(u->hw_dB_max));
-#endif
-
- if (u->hw_dB_min >= u->hw_dB_max)
- pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0);
- else {
- pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0);
- u->hw_dB_supported = TRUE;
-
- if (u->hw_dB_max > 0) {
- u->source->base_volume = pa_sw_volume_from_dB(- (double) u->hw_dB_max/100.0);
- pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->source->base_volume));
- } else
- pa_log_info("No particular base volume set, fixing to 0 dB");
-
- }
- }
-
- if (!u->hw_dB_supported &&
- u->hw_volume_max - u->hw_volume_min < 3) {
-
- pa_log_info("Device has less than 4 volume levels. Falling back to software volume control.");
- suitable = FALSE;
- }
- }
-
- if (suitable) {
- u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, FALSE) >= 0;
-
- u->source->get_volume = source_get_volume_cb;
- u->source->set_volume = source_set_volume_cb;
- u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0);
- pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
-
- if (!u->hw_dB_supported)
- u->source->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1;
- } else
- pa_log_info("Using software volume control.");
- }
-
- if (snd_mixer_selem_has_capture_switch(u->mixer_elem)) {
- u->source->get_mute = source_get_mute_cb;
- u->source->set_mute = source_set_mute_cb;
- u->source->flags |= PA_SOURCE_HW_MUTE_CTRL;
- } else
- pa_log_info("Using software mute control.");
-
- u->mixer_fdl = pa_alsa_fdlist_new();
-
- if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, m->core->mainloop) < 0) {
- pa_log("Failed to initialize file descriptor monitoring");
- goto fail;
- }
-
- snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback);
- snd_mixer_elem_set_callback_private(u->mixer_elem, u);
- } else
- u->mixer_fdl = NULL;
+ if (setup_mixer(u, ignore_dB) < 0)
+ goto fail;
pa_alsa_dump(u->pcm_handle);
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 5236d02f..d00a80f1 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1,7 +1,7 @@
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2009 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@@ -32,11 +32,13 @@
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
#include <pulse/util.h>
+#include <pulse/i18n.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include <pulsecore/atomic.h>
+#include <pulsecore/core-error.h>
#include "alsa-util.h"
@@ -112,7 +114,7 @@ static void io_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t
static void defer_cb(pa_mainloop_api*a, pa_defer_event* e, void *userdata) {
struct pa_alsa_fdlist *fdl = userdata;
unsigned num_fds, i;
- int err;
+ int err, n;
struct pollfd *temp;
pa_assert(a);
@@ -121,7 +123,11 @@ static void defer_cb(pa_mainloop_api*a, pa_defer_event* e, void *userdata) {
a->defer_enable(fdl->defer, 0);
- num_fds = (unsigned) snd_mixer_poll_descriptors_count(fdl->mixer);
+ if ((n = snd_mixer_poll_descriptors_count(fdl->mixer)) < 0) {
+ pa_log("snd_mixer_poll_descriptors_count() failed: %s", snd_strerror(n));
+ return;
+ }
+ num_fds = (unsigned) n;
if (num_fds != fdl->num_fds) {
if (fdl->fds)
@@ -342,7 +348,8 @@ int pa_alsa_set_hw_params(
goto finish;
if (_use_mmap) {
- if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) {
+
+ if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
/* mmap() didn't work, fall back to interleaved */
@@ -462,6 +469,7 @@ finish:
int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) {
snd_pcm_sw_params_t *swparams;
+ snd_pcm_uframes_t boundary;
int err;
pa_assert(pcm);
@@ -473,7 +481,22 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) {
return err;
}
- if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, (snd_pcm_uframes_t) -1)) < 0) {
+ if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, 0)) < 0) {
+ pa_log_warn("Unable to disable period event: %s\n", snd_strerror(err));
+ return err;
+ }
+
+ if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
+ pa_log_warn("Unable to enable time stamping: %s\n", snd_strerror(err));
+ return err;
+ }
+
+ if ((err = snd_pcm_sw_params_get_boundary(swparams, &boundary)) < 0) {
+ pa_log_warn("Unable to get boundary: %s\n", snd_strerror(err));
+ return err;
+ }
+
+ if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary)) < 0) {
pa_log_warn("Unable to set stop threshold: %s\n", snd_strerror(err));
return err;
}
@@ -499,39 +522,39 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) {
static const struct pa_alsa_profile_info device_table[] = {
{{ 1, { PA_CHANNEL_POSITION_MONO }},
"hw",
- "Analog Mono",
+ N_("Analog Mono"),
"analog-mono",
1 },
{{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
"front",
- "Analog Stereo",
+ N_("Analog Stereo"),
"analog-stereo",
10 },
{{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
"iec958",
- "IEC958 Digital Stereo",
+ N_("Digital Stereo (IEC958)"),
"iec958-stereo",
5 },
{{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
"hdmi",
- "HDMI Digital Stereo",
+ N_("Digital Stereo (HDMI)"),
"hdmi-stereo",
4 },
{{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }},
"surround40",
- "Analog Surround 4.0",
+ N_("Analog Surround 4.0"),
"analog-surround-40",
7 },
{{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }},
"a52",
- "IEC958/AC3 Digital Surround 4.0",
+ N_("Digital Surround 4.0 (IEC958/AC3)"),
"iec958-ac3-surround-40",
2 },
@@ -539,7 +562,7 @@ static const struct pa_alsa_profile_info device_table[] = {
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_LFE }},
"surround41",
- "Analog Surround 4.1",
+ N_("Analog Surround 4.1"),
"analog-surround-41",
7 },
@@ -547,7 +570,7 @@ static const struct pa_alsa_profile_info device_table[] = {
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_CENTER }},
"surround50",
- "Analog Surround 5.0",
+ N_("Analog Surround 5.0"),
"analog-surround-50",
7 },
@@ -555,7 +578,7 @@ static const struct pa_alsa_profile_info device_table[] = {
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }},
"surround51",
- "Analog Surround 5.1",
+ N_("Analog Surround 5.1"),
"analog-surround-51",
8 },
@@ -563,7 +586,7 @@ static const struct pa_alsa_profile_info device_table[] = {
PA_CHANNEL_POSITION_FRONT_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT,
PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE}},
"a52",
- "IEC958/AC3 Digital Surround 5.1",
+ N_("Digital Surround 5.1 (IEC958/AC3)"),
"iec958-ac3-surround-51",
3 },
@@ -572,7 +595,7 @@ static const struct pa_alsa_profile_info device_table[] = {
PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE,
PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }},
"surround71",
- "Analog Surround 7.1",
+ N_("Analog Surround 7.1"),
"analog-surround-71",
7 },
@@ -805,8 +828,7 @@ snd_pcm_t *pa_alsa_open_by_device_string(
SND_PCM_NO_AUTO_CHANNELS|
(reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) {
pa_log_info("Error opening PCM device %s: %s", d, snd_strerror(err));
- pa_xfree(d);
- return NULL;
+ goto fail;
}
if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, require_exact_channel_number)) < 0) {
@@ -834,9 +856,9 @@ snd_pcm_t *pa_alsa_open_by_device_string(
}
pa_log_info("Failed to set hardware parameters on %s: %s", d, snd_strerror(err));
- pa_xfree(d);
snd_pcm_close(pcm_handle);
- return NULL;
+
+ goto fail;
}
if (dev)
@@ -849,6 +871,11 @@ snd_pcm_t *pa_alsa_open_by_device_string(
return pcm_handle;
}
+
+fail:
+ pa_xfree(d);
+
+ return NULL;
}
int pa_alsa_probe_profiles(
@@ -1057,6 +1084,86 @@ success:
return elem;
}
+
+int pa_alsa_find_mixer_and_elem(
+ snd_pcm_t *pcm,
+ snd_mixer_t **_m,
+ snd_mixer_elem_t **_e) {
+
+ int err;
+ snd_mixer_t *m;
+ snd_mixer_elem_t *e;
+ pa_bool_t found = FALSE;
+ const char *dev;
+
+ pa_assert(pcm);
+ pa_assert(_m);
+ pa_assert(_e);
+
+ if ((err = snd_mixer_open(&m, 0)) < 0) {
+ pa_log("Error opening mixer: %s", snd_strerror(err));
+ return -1;
+ }
+
+ /* First, try by name */
+ if ((dev = snd_pcm_name(pcm)))
+ if (pa_alsa_prepare_mixer(m, dev) >= 0)
+ found = TRUE;
+
+ /* Then, try by card index */
+ if (!found) {
+ snd_pcm_info_t* info;
+ snd_pcm_info_alloca(&info);
+
+ if (snd_pcm_info(pcm, info) >= 0) {
+ char *md;
+ int card_idx;
+
+ if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
+
+ md = pa_sprintf_malloc("hw:%i", card_idx);
+
+ if (!dev || !pa_streq(dev, md))
+ if (pa_alsa_prepare_mixer(m, md) >= 0)
+ found = TRUE;
+
+ pa_xfree(md);
+ }
+ }
+ }
+
+ if (!found) {
+ snd_mixer_close(m);
+ return -1;
+ }
+
+ switch (snd_pcm_stream(pcm)) {
+
+ case SND_PCM_STREAM_PLAYBACK:
+ e = pa_alsa_find_elem(m, "Master", "PCM", TRUE);
+ break;
+
+ case SND_PCM_STREAM_CAPTURE:
+ e = pa_alsa_find_elem(m, "Capture", "Mic", FALSE);
+ break;
+
+ default:
+ pa_assert_not_reached();
+ }
+
+ if (!e) {
+ snd_mixer_close(m);
+ return -1;
+ }
+
+ pa_assert(e && m);
+
+ *_m = m;
+ *_e = e;
+
+ return 0;
+}
+
static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
[PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
@@ -1281,7 +1388,7 @@ void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card) {
#endif
}
-void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info) {
+void pa_alsa_init_proplist_pcm_info(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info) {
static const char * const alsa_class_table[SND_PCM_CLASS_LAST+1] = {
[SND_PCM_CLASS_GENERIC] = "generic",
@@ -1302,7 +1409,7 @@ void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_i
snd_pcm_class_t class;
snd_pcm_subclass_t subclass;
- const char *n, *id, *sdn, *cn;
+ const char *n, *id, *sdn, *cn = NULL;
int card;
pa_assert(p);
@@ -1347,6 +1454,28 @@ void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_i
pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, n);
}
+void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm) {
+ snd_pcm_hw_params_t *hwparams;
+ snd_pcm_info_t *info;
+ int bits, err;
+
+ snd_pcm_hw_params_alloca(&hwparams);
+ snd_pcm_info_alloca(&info);
+
+ if ((err = snd_pcm_hw_params_current(pcm, hwparams)) < 0)
+ pa_log_warn("Error fetching hardware parameter info: %s", snd_strerror(err));
+ else {
+
+ if ((bits = snd_pcm_hw_params_get_sbits(hwparams)) >= 0)
+ pa_proplist_setf(p, "alsa.resolution_bits", "%i", bits);
+ }
+
+ if ((err = snd_pcm_info(pcm, info)) < 0)
+ pa_log_warn("Error fetching PCM info: %s", snd_strerror(err));
+ else
+ pa_alsa_init_proplist_pcm_info(c, p, info);
+}
+
int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
snd_pcm_state_t state;
int err;
@@ -1425,7 +1554,7 @@ pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) {
return item;
}
-snd_pcm_sframes_t pa_alsa_safe_avail_update(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss) {
+snd_pcm_sframes_t pa_alsa_safe_avail(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss) {
snd_pcm_sframes_t n;
size_t k;
@@ -1436,7 +1565,7 @@ snd_pcm_sframes_t pa_alsa_safe_avail_update(snd_pcm_t *pcm, size_t hwbuf_size, c
/* Some ALSA driver expose weird bugs, let's inform the user about
* what is going on */
- n = snd_pcm_avail_update(pcm);
+ n = snd_pcm_avail(pcm);
if (n <= 0)
return n;
@@ -1445,8 +1574,8 @@ snd_pcm_sframes_t pa_alsa_safe_avail_update(snd_pcm_t *pcm, size_t hwbuf_size, c
if (k >= hwbuf_size * 3 ||
k >= pa_bytes_per_second(ss)*10)
- pa_log("snd_pcm_avail_update() returned a value that is exceptionally large: %lu bytes (%lu ms) "
- "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.",
+ pa_log(_("snd_pcm_avail_update() returned a value that is exceptionally large: %lu bytes (%lu ms). "
+ "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers."),
(unsigned long) k, (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC));
return n;
@@ -1477,8 +1606,8 @@ int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas
k >= hwbuf_size * 3 ||
k >= pa_bytes_per_second(ss)*10)
- pa_log("snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms) "
- "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.",
+ pa_log(_("snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms). "
+ "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers."),
(unsigned long) k, (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC));
return r;
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index 8a209348..2d0f407e 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -54,11 +54,12 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min);
int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev);
snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback, pa_bool_t playback);
+int pa_alsa_find_mixer_and_elem(snd_pcm_t *pcm, snd_mixer_t **_m, snd_mixer_elem_t **_e);
typedef struct pa_alsa_profile_info {
pa_channel_map map;
const char *alsa_name;
- const char *description;
+ const char *description; /* internationalized */
const char *name;
unsigned priority;
} pa_alsa_profile_info;
@@ -119,14 +120,15 @@ void pa_alsa_dump_status(snd_pcm_t *pcm);
void pa_alsa_redirect_errors_inc(void);
void pa_alsa_redirect_errors_dec(void);
-void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info);
+void pa_alsa_init_proplist_pcm_info(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info);
void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card);
+void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm);
int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents);
pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll);
-snd_pcm_sframes_t pa_alsa_safe_avail_update(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss);
+snd_pcm_sframes_t pa_alsa_safe_avail(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss);
int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss);
char *pa_alsa_get_driver_name(int card);
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index e63414ec..e517ddcc 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -24,6 +24,8 @@
#endif
#include <pulse/xmalloc.h>
+#include <pulse/i18n.h>
+
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/queue.h>
@@ -104,14 +106,14 @@ static void enumerate_cb(
if (sink && source) {
n = pa_sprintf_malloc("output-%s+input-%s", sink->name, source->name);
- t = pa_sprintf_malloc("Output %s + Input %s", sink->description, source->description);
+ t = pa_sprintf_malloc(_("Output %s + Input %s"), sink->description, _(source->description));
} else if (sink) {
n = pa_sprintf_malloc("output-%s", sink->name);
- t = pa_sprintf_malloc("Output %s", sink->description);
+ t = pa_sprintf_malloc(_("Output %s"), _(sink->description));
} else {
pa_assert(source);
n = pa_sprintf_malloc("input-%s", source->name);
- t = pa_sprintf_malloc("Input %s", source->description);
+ t = pa_sprintf_malloc(_("Input %s"), _(source->description));
}
pa_log_info("Found output profile '%s'", t);
@@ -142,7 +144,7 @@ static void add_disabled_profile(pa_hashmap *profiles) {
pa_card_profile *p;
struct profile_data *d;
- p = pa_card_profile_new("off", "Off", sizeof(struct profile_data));
+ p = pa_card_profile_new("off", _("Off"), sizeof(struct profile_data));
d = PA_CARD_PROFILE_DATA(p);
d->sink_profile = d->source_profile = NULL;
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index b04834da..ac8344f0 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -33,6 +33,8 @@
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
#include <pulse/sample.h>
+#include <pulse/i18n.h>
+
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
#include <pulsecore/core-util.h>
@@ -154,89 +156,98 @@ struct userdata {
pa_bluetooth_device *device;
- int write_type, read_type;
+ int stream_write_type, stream_read_type;
+ int service_write_type, service_read_type;
};
static int init_bt(struct userdata *u);
static int init_profile(struct userdata *u);
-static int service_send(int fd, const bt_audio_msg_header_t *msg) {
- size_t length;
+static int service_send(struct userdata *u, const bt_audio_msg_header_t *msg) {
ssize_t r;
- pa_assert(fd >= 0);
+ pa_assert(u);
+ pa_assert(u->service_fd >= 0);
pa_assert(msg);
-
- length = msg->length ? msg->length : BT_SUGGESTED_BUFFER_SIZE;
+ pa_assert(msg->length > 0);
pa_log_debug("Sending %s -> %s",
pa_strnull(bt_audio_strtype(msg->type)),
pa_strnull(bt_audio_strname(msg->name)));
- if ((r = send(fd, msg, length, 0)) == (ssize_t) length)
+ if ((r = pa_loop_write(u->service_fd, msg, msg->length, &u->service_write_type)) == (ssize_t) msg->length)
return 0;
if (r < 0)
pa_log_error("Error sending data to audio service: %s", pa_cstrerror(errno));
else
- pa_log_error("Short send()");
+ pa_log_error("Short write()");
return -1;
}
-static int service_recv(int fd, bt_audio_msg_header_t *msg, size_t expected_length) {
- size_t length;
+static int service_recv(struct userdata *u, bt_audio_msg_header_t *msg, size_t room) {
ssize_t r;
- pa_assert(fd >= 0);
+ pa_assert(u);
+ pa_assert(u->service_fd >= 0);
pa_assert(msg);
- length = expected_length ? expected_length : BT_SUGGESTED_BUFFER_SIZE;
-
- if (length < sizeof(bt_audio_error_t))
- length = sizeof(bt_audio_error_t);
+ if (room <= 0)
+ room = BT_SUGGESTED_BUFFER_SIZE;
pa_log_debug("Trying to receive message from audio service...");
- r = recv(fd, msg, length, 0);
-
- if (r > 0 && (r == (ssize_t) length || expected_length <= 0)) {
+ /* First, read the header */
+ if ((r = pa_loop_read(u->service_fd, msg, sizeof(*msg), &u->service_read_type)) != sizeof(*msg))
+ goto read_fail;
- if ((size_t) r < sizeof(*msg)) {
- pa_log_error("Packet read too small.");
- return -1;
- }
+ if (msg->length < sizeof(*msg)) {
+ pa_log_error("Invalid message size.");
+ return -1;
+ }
- if (r != msg->length) {
- pa_log_error("Size read doesn't match header size.");
- return -1;
- }
+ /* Secondly, read the payload */
+ if (msg->length > sizeof(*msg)) {
- pa_log_debug("Received %s <- %s",
- pa_strnull(bt_audio_strtype(msg->type)),
- pa_strnull(bt_audio_strname(msg->name)));
+ size_t remains = msg->length - sizeof(*msg);
- return 0;
+ if ((r = pa_loop_read(u->service_fd,
+ (uint8_t*) msg + sizeof(*msg),
+ remains,
+ &u->service_read_type)) != (ssize_t) remains)
+ goto read_fail;
}
+ pa_log_debug("Received %s <- %s",
+ pa_strnull(bt_audio_strtype(msg->type)),
+ pa_strnull(bt_audio_strname(msg->name)));
+
+ return 0;
+
+read_fail:
+
if (r < 0)
pa_log_error("Error receiving data from audio service: %s", pa_cstrerror(errno));
else
- pa_log_error("Short recv()");
+ pa_log_error("Short read()");
return -1;
}
-static ssize_t service_expect(int fd, bt_audio_msg_header_t *rsp, uint8_t expected_name, size_t expected_length) {
+static ssize_t service_expect(struct userdata*u, bt_audio_msg_header_t *rsp, size_t room, uint8_t expected_name, size_t expected_size) {
int r;
- pa_assert(fd >= 0);
+ pa_assert(u);
+ pa_assert(u->service_fd >= 0);
pa_assert(rsp);
- if ((r = service_recv(fd, rsp, expected_length)) < 0)
+ if ((r = service_recv(u, rsp, room)) < 0)
return r;
- if (rsp->type != BT_RESPONSE || rsp->name != expected_name) {
+ if ((rsp->type != BT_INDICATION && rsp->type != BT_RESPONSE) ||
+ rsp->name != expected_name ||
+ (expected_size > 0 && rsp->length != expected_size)) {
if (rsp->type == BT_ERROR && rsp->length == sizeof(bt_audio_error_t))
pa_log_error("Received error condition: %s", pa_cstrerror(((bt_audio_error_t*) rsp)->posix_errno));
@@ -328,10 +339,10 @@ static int get_caps(struct userdata *u) {
}
msg.getcaps_req.flags = BT_FLAG_AUTOCONNECT;
- if (service_send(u->service_fd, &msg.getcaps_req.h) < 0)
+ if (service_send(u, &msg.getcaps_req.h) < 0)
return -1;
- if (service_expect(u->service_fd, &msg.getcaps_rsp.h, BT_GET_CAPABILITIES, 0) < 0)
+ if (service_expect(u, &msg.getcaps_rsp.h, sizeof(msg), BT_GET_CAPABILITIES, 0) < 0)
return -1;
return parse_caps(u, &msg.getcaps_rsp);
@@ -610,10 +621,10 @@ static int set_conf(struct userdata *u) {
msg.setconf_req.h.length += msg.setconf_req.codec.length - sizeof(msg.setconf_req.codec);
}
- if (service_send(u->service_fd, &msg.setconf_req.h) < 0)
+ if (service_send(u, &msg.setconf_req.h) < 0)
return -1;
- if (service_expect(u->service_fd, &msg.setconf_rsp.h, BT_SET_CONFIGURATION, sizeof(msg.setconf_rsp)) < 0)
+ if (service_expect(u, &msg.setconf_rsp.h, sizeof(msg), BT_SET_CONFIGURATION, sizeof(msg.setconf_rsp)) < 0)
return -1;
if ((u->profile == PROFILE_A2DP && msg.setconf_rsp.transport != BT_CAPABILITIES_TRANSPORT_A2DP) ||
@@ -660,13 +671,13 @@ static int setup_stream_fd(struct userdata *u) {
msg.start_req.h.name = BT_START_STREAM;
msg.start_req.h.length = sizeof(msg.start_req);
- if (service_send(u->service_fd, &msg.start_req.h) < 0)
+ if (service_send(u, &msg.start_req.h) < 0)
return -1;
- if (service_expect(u->service_fd, &msg.rsp, BT_START_STREAM, sizeof(msg.start_rsp)) < 0)
+ if (service_expect(u, &msg.rsp, sizeof(msg), BT_START_STREAM, sizeof(msg.start_rsp)) < 0)
return -1;
- if (service_expect(u->service_fd, &msg.rsp, BT_NEW_STREAM, sizeof(msg.streamfd_ind)) < 0)
+ if (service_expect(u, &msg.rsp, sizeof(msg), BT_NEW_STREAM, sizeof(msg.streamfd_ind)) < 0)
return -1;
if ((u->stream_fd = bt_audio_service_get_data_fd(u->service_fd)) < 0) {
@@ -776,7 +787,7 @@ static int hsp_process_render(struct userdata *u) {
const void *p;
p = (const uint8_t*) pa_memblock_acquire(memchunk.memblock) + memchunk.index;
- l = pa_write(u->stream_fd, p, memchunk.length, &u->write_type);
+ l = pa_write(u->stream_fd, p, memchunk.length, &u->stream_write_type);
pa_memblock_release(memchunk.memblock);
pa_log_debug("Memblock written to socket: %lli bytes", (long long) l);
@@ -825,7 +836,7 @@ static int hsp_process_push(struct userdata *u) {
void *p;
p = pa_memblock_acquire(memchunk.memblock);
- l = pa_read(u->stream_fd, p, pa_memblock_get_length(memchunk.memblock), &u->read_type);
+ l = pa_read(u->stream_fd, p, pa_memblock_get_length(memchunk.memblock), &u->stream_read_type);
pa_memblock_release(memchunk.memblock);
if (l <= 0) {
@@ -895,6 +906,11 @@ static int a2dp_process_render(struct userdata *u) {
(void*) p, u->write_memchunk.length,
d, left,
&written);
+
+ PA_ONCE_BEGIN {
+ pa_log_debug("Using SBC encoder implementation: %s", pa_strnull(sbc_get_implementation_info(&a2dp->sbc)));
+ } PA_ONCE_END;
+
pa_memblock_release(u->write_memchunk.memblock);
if (encoded <= 0) {
@@ -941,7 +957,7 @@ static int a2dp_process_render(struct userdata *u) {
for (;;) {
ssize_t l;
- l = pa_write(u->stream_fd, p, left, &u->write_type);
+ l = pa_write(u->stream_fd, p, left, &u->stream_write_type);
/* pa_log_debug("write: requested %lu bytes; written %li bytes; mtu=%li", (unsigned long) left, (long) l, (unsigned long) u->link_mtu); */
pa_assert(l != 0);
@@ -1340,9 +1356,6 @@ static int add_sink(struct userdata *u) {
u->sink->userdata = u;
u->sink->parent.process_msg = sink_process_msg;
-
- pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
- pa_sink_set_rtpoll(u->sink, u->rtpoll);
}
/* u->sink->get_volume = sink_get_volume_cb; */
@@ -1387,9 +1400,6 @@ static int add_source(struct userdata *u) {
u->source->userdata = u;
u->source->parent.process_msg = source_process_msg;
-
- pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
- pa_source_set_rtpoll(u->source, u->rtpoll);
}
/* u->source->get_volume = source_get_volume_cb; */
@@ -1403,10 +1413,9 @@ static int add_source(struct userdata *u) {
return 0;
}
-static int init_bt(struct userdata *u) {
+static void shutdown_bt(struct userdata *u) {
pa_assert(u);
- /* shutdown bt */
if (u->stream_fd >= 0) {
pa_close(u->stream_fd);
u->stream_fd = -1;
@@ -1416,14 +1425,21 @@ static int init_bt(struct userdata *u) {
pa_close(u->service_fd);
u->service_fd = -1;
}
+}
- u->write_type = u->read_type = 0;
+static int init_bt(struct userdata *u) {
+ pa_assert(u);
+
+ shutdown_bt(u);
+
+ u->stream_write_type = u->stream_read_type = 0;
+ u->service_write_type = u->service_write_type = 0;
- /* connect to the bluez audio service */
if ((u->service_fd = bt_audio_service_open()) < 0) {
pa_log_error("Couldn't connect to bluetooth audio service");
return -1;
}
+
pa_log_debug("Connected to the bluetooth audio service");
return 0;
@@ -1507,6 +1523,13 @@ static void stop_thread(struct userdata *u) {
pa_source_unref(u->source);
u->source = NULL;
}
+
+ if (u->rtpoll) {
+ pa_thread_mq_done(&u->thread_mq);
+
+ pa_rtpoll_free(u->rtpoll);
+ u->rtpoll = NULL;
+ }
}
static int start_thread(struct userdata *u) {
@@ -1514,6 +1537,7 @@ static int start_thread(struct userdata *u) {
pa_assert(u);
pa_assert(!u->thread);
+ pa_assert(!u->rtpoll);
pa_assert(!u->rtpoll_item);
if (USE_SCO_OVER_PCM(u)) {
@@ -1522,6 +1546,9 @@ static int start_thread(struct userdata *u) {
return 0;
}
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll);
+
u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
pollfd->fd = u->stream_fd;
@@ -1533,11 +1560,17 @@ static int start_thread(struct userdata *u) {
return -1;
}
- if (u->sink)
+ if (u->sink) {
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
pa_sink_put(u->sink);
+ }
- if (u->source)
+ if (u->source) {
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
pa_source_put(u->source);
+ }
return 0;
}
@@ -1566,7 +1599,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
}
stop_thread(u);
- init_bt(u);
+ shutdown_bt(u);
if (u->write_memchunk.memblock) {
pa_memblock_unref(u->write_memchunk.memblock);
@@ -1576,6 +1609,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
u->profile = *d;
u->sample_spec = u->requested_sample_spec;
+ init_bt(u);
init_profile(u);
if (u->sink || u->source)
@@ -1628,7 +1662,7 @@ static int add_card(struct userdata *u, const char * default_profile) {
data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
if (u->device->audio_sink_info_valid > 0) {
- p = pa_card_profile_new("a2dp", "A2DP Advanced Audio Distribution Profile", sizeof(enum profile));
+ p = pa_card_profile_new("a2dp", _("High Fidelity Playback (A2DP)"), sizeof(enum profile));
p->priority = 10;
p->n_sinks = 1;
p->n_sources = 0;
@@ -1642,7 +1676,7 @@ static int add_card(struct userdata *u, const char * default_profile) {
}
if (u->device->headset_info_valid > 0) {
- p = pa_card_profile_new("hsp", "HSP/HFP Headset/Hands-Free Profile", sizeof(enum profile));
+ p = pa_card_profile_new("hsp", _("Telephony Duplex (HSP/HFP)"), sizeof(enum profile));
p->priority = 20;
p->n_sinks = 1;
p->n_sources = 1;
@@ -1657,7 +1691,7 @@ static int add_card(struct userdata *u, const char * default_profile) {
pa_assert(!pa_hashmap_isempty(data.profiles));
- p = pa_card_profile_new("off", "Off", sizeof(enum profile));
+ p = pa_card_profile_new("off", _("Off"), sizeof(enum profile));
d = PA_CARD_PROFILE_DATA(p);
*d = PROFILE_OFF;
pa_hashmap_put(data.profiles, p->name, p);
@@ -1748,8 +1782,6 @@ int pa__init(pa_module* m) {
u->service_fd = -1;
u->stream_fd = -1;
u->read_smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
- u->rtpoll = pa_rtpoll_new();
- pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll);
u->sample_spec = m->core->default_sample_spec;
u->modargs = ma;
@@ -1905,19 +1937,10 @@ void pa__done(pa_module *m) {
if (u->card)
pa_card_free(u->card);
- pa_thread_mq_done(&u->thread_mq);
-
- if (u->rtpoll)
- pa_rtpoll_free(u->rtpoll);
-
if (u->read_smoother)
pa_smoother_free(u->read_smoother);
- if (u->stream_fd >= 0)
- pa_close(u->stream_fd);
-
- if (u->service_fd >= 0)
- pa_close(u->service_fd);
+ shutdown_bt(u);
if (u->device)
pa_bluetooth_device_free(u->device);
diff --git a/src/modules/bluetooth/sbc.c b/src/modules/bluetooth/sbc.c
index 29258d05..a33ed571 100644
--- a/src/modules/bluetooth/sbc.c
+++ b/src/modules/bluetooth/sbc.c
@@ -985,7 +985,7 @@ int sbc_decode(sbc_t *sbc, void *input, int input_len, void *output,
char *ptr;
int i, ch, framelen, samples;
- if (!sbc && !input)
+ if (!sbc || !input)
return -EIO;
priv = sbc->priv;
@@ -1053,7 +1053,7 @@ int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output,
const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
int nsamples, int nchannels);
- if (!sbc && !input)
+ if (!sbc || !input)
return -EIO;
priv = sbc->priv;
@@ -1221,6 +1221,20 @@ uint16_t sbc_get_codesize(sbc_t *sbc)
return subbands * blocks * channels * 2;
}
+const char *sbc_get_implementation_info(sbc_t *sbc)
+{
+ struct sbc_priv *priv;
+
+ if (!sbc)
+ return NULL;
+
+ priv = sbc->priv;
+ if (!priv)
+ return NULL;
+
+ return priv->enc_state.implementation_info;
+}
+
int sbc_reinit(sbc_t *sbc, unsigned long flags)
{
struct sbc_priv *priv;
diff --git a/src/modules/bluetooth/sbc.h b/src/modules/bluetooth/sbc.h
index b0a14888..f9d506bc 100644
--- a/src/modules/bluetooth/sbc.h
+++ b/src/modules/bluetooth/sbc.h
@@ -89,6 +89,7 @@ int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output,
int sbc_get_frame_length(sbc_t *sbc);
int sbc_get_frame_duration(sbc_t *sbc);
uint16_t sbc_get_codesize(sbc_t *sbc);
+const char *sbc_get_implementation_info(sbc_t *sbc);
void sbc_finish(sbc_t *sbc);
#ifdef __cplusplus
diff --git a/src/modules/bluetooth/sbc_primitives.c b/src/modules/bluetooth/sbc_primitives.c
index 303f3fee..6b0be3f5 100644
--- a/src/modules/bluetooth/sbc_primitives.c
+++ b/src/modules/bluetooth/sbc_primitives.c
@@ -456,6 +456,7 @@ void sbc_init_primitives(struct sbc_encoder_state *state)
/* Default implementation for scale factors calculation */
state->sbc_calc_scalefactors = sbc_calc_scalefactors;
+ state->implementation_info = "Generic C";
/* X86/AMD64 optimizations */
#ifdef SBC_BUILD_WITH_MMX_SUPPORT
diff --git a/src/modules/bluetooth/sbc_primitives.h b/src/modules/bluetooth/sbc_primitives.h
index 2708c829..3d01c115 100644
--- a/src/modules/bluetooth/sbc_primitives.h
+++ b/src/modules/bluetooth/sbc_primitives.h
@@ -62,6 +62,7 @@ struct sbc_encoder_state {
void (*sbc_calc_scalefactors)(int32_t sb_sample_f[16][2][8],
uint32_t scale_factor[2][8],
int blocks, int channels, int subbands);
+ const char *implementation_info;
};
/*
diff --git a/src/modules/bluetooth/sbc_primitives_mmx.c b/src/modules/bluetooth/sbc_primitives_mmx.c
index 1870a9ba..08e9ca28 100644
--- a/src/modules/bluetooth/sbc_primitives_mmx.c
+++ b/src/modules/bluetooth/sbc_primitives_mmx.c
@@ -313,6 +313,7 @@ void sbc_init_primitives_mmx(struct sbc_encoder_state *state)
if (check_mmx_support()) {
state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_mmx;
state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_mmx;
+ state->implementation_info = "MMX";
}
}
diff --git a/src/modules/bluetooth/sbc_primitives_neon.c b/src/modules/bluetooth/sbc_primitives_neon.c
index d9c12f9e..f1bc7b48 100644
--- a/src/modules/bluetooth/sbc_primitives_neon.c
+++ b/src/modules/bluetooth/sbc_primitives_neon.c
@@ -240,6 +240,7 @@ void sbc_init_primitives_neon(struct sbc_encoder_state *state)
{
state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_neon;
state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_neon;
+ state->implementation_info = "NEON";
}
#endif
diff --git a/src/modules/dbus-util.c b/src/modules/dbus-util.c
index f6a986a5..4218bca5 100644
--- a/src/modules/dbus-util.c
+++ b/src/modules/dbus-util.c
@@ -403,8 +403,7 @@ void pa_dbus_pending_free(pa_dbus_pending *p) {
pa_assert(p);
if (p->pending) {
- dbus_pending_call_cancel(p->pending);
- dbus_pending_call_unref(p->pending);
+ dbus_pending_call_cancel(p->pending); /* p->pending is freed by cancel() */
}
if (p->message)
diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
index 4dffd365..c7696058 100644
--- a/src/modules/module-card-restore.c
+++ b/src/modules/module-card-restore.c
@@ -70,10 +70,10 @@ struct userdata {
#define ENTRY_VERSION 1
-struct entry PA_GCC_PACKED {
+struct entry {
uint8_t version;
char profile[PA_NAME_MAX];
-};
+} PA_GCC_PACKED ;
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
struct userdata *u = userdata;
@@ -191,7 +191,7 @@ static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new
pa_assert(new_data);
- if ((e = read_entry(u, new_data->name)) && e->profile) {
+ if ((e = read_entry(u, new_data->name)) && e->profile[0]) {
if (!new_data->active_profile) {
pa_card_new_data_set_profile(new_data, e->profile);
diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c
index 9ed262db..773e1d87 100644
--- a/src/modules/module-detect.c
+++ b/src/modules/module-detect.c
@@ -100,7 +100,7 @@ static int detect_alsa(pa_core *c, int just_one) {
if (subdevice != 0)
continue;
- pa_snprintf(args, sizeof(args), "device=hw:%u", device);
+ pa_snprintf(args, sizeof(args), "device_id=%u", device);
if (!pa_module_load(c, is_sink ? "module-alsa-sink" : "module-alsa-source", args))
continue;
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index 7c56c240..e6a48814 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -81,12 +81,12 @@ struct userdata {
#define ENTRY_VERSION 1
-struct entry PA_GCC_PACKED {
+struct entry {
uint8_t version;
pa_bool_t muted:1;
pa_channel_map channel_map;
pa_cvolume volume;
-};
+} PA_GCC_PACKED;
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
struct userdata *u = userdata;
diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c
index e6037381..ce04f367 100644
--- a/src/modules/module-hal-detect.c
+++ b/src/modules/module-hal-detect.c
@@ -370,6 +370,7 @@ static struct device* hal_device_add(struct userdata *u, const char *udi) {
d->originating_udi = NULL;
d->module = PA_INVALID_INDEX;
d->sink_name = d->source_name = d->card_name = NULL;
+ r = -1;
#ifdef HAVE_ALSA
if (pa_streq(u->capability, CAPABILITY_ALSA))
diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c
index aefd4020..ca9274d8 100644
--- a/src/modules/module-protocol-stub.c
+++ b/src/modules/module-protocol-stub.c
@@ -211,7 +211,9 @@ struct userdata {
#if defined(USE_TCP_SOCKETS)
pa_socket_server *socket_server_ipv4;
+# ifdef HAVE_IPV6
pa_socket_server *socket_server_ipv6;
+# endif
#else
pa_socket_server *socket_server_unix;
char *socket_path;
@@ -299,20 +301,30 @@ int pa__init(pa_module*m) {
listen_on = pa_modargs_get_value(ma, "listen", NULL);
if (listen_on) {
+# ifdef HAVE_IPV6
u->socket_server_ipv6 = pa_socket_server_new_ipv6_string(m->core->mainloop, listen_on, (uint16_t) port, TCPWRAP_SERVICE);
+# endif
u->socket_server_ipv4 = pa_socket_server_new_ipv4_string(m->core->mainloop, listen_on, (uint16_t) port, TCPWRAP_SERVICE);
} else {
+# ifdef HAVE_IPV6
u->socket_server_ipv6 = pa_socket_server_new_ipv6_any(m->core->mainloop, (uint16_t) port, TCPWRAP_SERVICE);
+# endif
u->socket_server_ipv4 = pa_socket_server_new_ipv4_any(m->core->mainloop, (uint16_t) port, TCPWRAP_SERVICE);
}
+# ifdef HAVE_IPV6
if (!u->socket_server_ipv4 && !u->socket_server_ipv6)
+# else
+ if (!u->socket_server_ipv4)
+# endif
goto fail;
if (u->socket_server_ipv4)
pa_socket_server_set_callback(u->socket_server_ipv4, socket_server_on_connection_cb, u);
+# ifdef HAVE_IPV6
if (u->socket_server_ipv6)
pa_socket_server_set_callback(u->socket_server_ipv6, socket_server_on_connection_cb, u);
+# endif
#else
@@ -358,9 +370,11 @@ int pa__init(pa_module*m) {
if (pa_socket_server_get_address(u->socket_server_ipv4, t, sizeof(t)))
pa_native_protocol_add_server_string(u->native_protocol, t);
+# ifdef HAVE_IPV6
if (u->socket_server_ipv6)
if (pa_socket_server_get_address(u->socket_server_ipv6, t, sizeof(t)))
pa_native_protocol_add_server_string(u->native_protocol, t);
+# endif
# else
if (pa_socket_server_get_address(u->socket_server_unix, t, sizeof(t)))
pa_native_protocol_add_server_string(u->native_protocol, t);
@@ -418,9 +432,11 @@ void pa__done(pa_module*m) {
if (pa_socket_server_get_address(u->socket_server_ipv4, t, sizeof(t)))
pa_native_protocol_remove_server_string(u->native_protocol, t);
+# ifdef HAVE_IPV6
if (u->socket_server_ipv6)
if (pa_socket_server_get_address(u->socket_server_ipv6, t, sizeof(t)))
pa_native_protocol_remove_server_string(u->native_protocol, t);
+# endif
# else
if (u->socket_server_unix)
if (pa_socket_server_get_address(u->socket_server_unix, t, sizeof(t)))
@@ -444,8 +460,10 @@ void pa__done(pa_module*m) {
#if defined(USE_TCP_SOCKETS)
if (u->socket_server_ipv4)
pa_socket_server_unref(u->socket_server_ipv4);
+# ifdef HAVE_IPV6
if (u->socket_server_ipv6)
pa_socket_server_unref(u->socket_server_ipv6);
+# endif
#else
if (u->socket_server_unix)
pa_socket_server_unref(u->socket_server_unix);
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 2dd2045e..d935caf6 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -93,7 +93,7 @@ struct userdata {
#define ENTRY_VERSION 1
-struct entry PA_GCC_PACKED {
+struct entry {
uint8_t version;
pa_bool_t muted_valid:1, relative_volume_valid:1, absolute_volume_valid:1, device_valid:1;
pa_bool_t muted:1;
@@ -101,7 +101,7 @@ struct entry PA_GCC_PACKED {
pa_cvolume relative_volume;
pa_cvolume absolute_volume;
char device[PA_NAME_MAX];
-};
+} PA_GCC_PACKED;
enum {
SUBCOMMAND_TEST,
@@ -532,6 +532,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
pa_xfree(n);
continue;
}
+ pa_xfree(n);
if (u->restore_volume) {
pa_cvolume v;
@@ -581,6 +582,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
pa_xfree(n);
continue;
}
+ pa_xfree(n);
if (u->restore_device &&
e->device_valid &&
diff --git a/src/modules/module-x11-cork-request.c b/src/modules/module-x11-cork-request.c
new file mode 100644
index 00000000..0c9aedf4
--- /dev/null
+++ b/src/modules/module-x11-cork-request.c
@@ -0,0 +1,189 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <X11/Xlib.h>
+#include <X11/extensions/XTest.h>
+#include <X11/XF86keysym.h>
+#include <X11/keysym.h>
+
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/x11wrap.h>
+#include <pulsecore/core-util.h>
+
+#include "module-x11-cork-request-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Synthesize X11 media key events when cork/uncork is requested");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE("display=<X11 display>");
+
+static const char* const valid_modargs[] = {
+ "display",
+ NULL
+};
+
+struct userdata {
+ pa_module *module;
+
+ pa_x11_wrapper *x11_wrapper;
+ pa_x11_client *x11_client;
+
+ pa_hook_slot *hook_slot;
+};
+
+static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(w);
+ pa_assert(u);
+ pa_assert(u->x11_wrapper == w);
+
+ if (u->x11_client) {
+ pa_x11_client_free(u->x11_client);
+ u->x11_client = NULL;
+ }
+
+ if (u->x11_wrapper) {
+ pa_x11_wrapper_unref(u->x11_wrapper);
+ u->x11_wrapper = NULL;
+ }
+
+ pa_module_unload_request(u->module, TRUE);
+}
+
+static pa_hook_result_t sink_input_send_event_hook_cb(
+ pa_core *c,
+ pa_sink_input_send_event_hook_data *data,
+ struct userdata *u) {
+
+ KeySym sym;
+ KeyCode code;
+ Display *display;
+
+ pa_assert(c);
+ pa_assert(data);
+ pa_assert(u);
+
+ if (pa_streq(data->event, PA_STREAM_EVENT_REQUEST_CORK))
+ sym = XF86XK_AudioPause;
+ else if (pa_streq(data->event, PA_STREAM_EVENT_REQUEST_UNCORK))
+ sym = XF86XK_AudioPlay;
+ else
+ return PA_HOOK_OK;
+
+ pa_log_debug("Triggering X11 keysym: %s", XKeysymToString(sym));
+
+ display = pa_x11_wrapper_get_display(u->x11_wrapper);
+ code = XKeysymToKeycode(display, sym);
+
+ XTestFakeKeyEvent(display, code, True, CurrentTime);
+ XSync(display, False);
+
+ XTestFakeKeyEvent(display, code, False, CurrentTime);
+ XSync(display, False);
+
+ return PA_HOOK_OK;
+}
+
+int pa__init(pa_module *m) {
+ struct userdata *u;
+ pa_modargs *ma;
+ int xtest_event_base, xtest_error_base;
+ int major_version, minor_version;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("failed to parse module arguments");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->module = m;
+
+ if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
+ goto fail;
+
+ if (!XTestQueryExtension(
+ pa_x11_wrapper_get_display(u->x11_wrapper),
+ &xtest_event_base, &xtest_error_base,
+ &major_version, &minor_version)) {
+
+ pa_log("XTest extension not supported.");
+ goto fail;
+ }
+
+ pa_log_debug("XTest %i.%i supported.", major_version, minor_version);
+
+ u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u);
+
+ u->hook_slot = pa_hook_connect(
+ &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT],
+ PA_HOOK_NORMAL,
+ (pa_hook_cb_t) sink_input_send_event_hook_cb, u);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(m);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata*u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->x11_client)
+ pa_x11_client_free(u->x11_client);
+
+ if (u->x11_wrapper)
+ pa_x11_wrapper_unref(u->x11_wrapper);
+
+ if (u->hook_slot)
+ pa_hook_slot_free(u->hook_slot);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-x11-publish.c b/src/modules/module-x11-publish.c
index c6c5bacd..fb27eba2 100644
--- a/src/modules/module-x11-publish.c
+++ b/src/modules/module-x11-publish.c
@@ -55,7 +55,11 @@ PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("X11 credential publisher");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(FALSE);
-PA_MODULE_USAGE("display=<X11 display>");
+PA_MODULE_USAGE(
+ "display=<X11 display> "
+ "sink=<Sink to publish> "
+ "source=<Source to publish> "
+ "cookie=<Cookie file to publish> ");
static const char* const valid_modargs[] = {
"display",
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index c118b5c6..0d86459e 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -373,11 +373,13 @@ static int mcast_socket(const struct sockaddr* sa, socklen_t salen) {
memset(&mr4, 0, sizeof(mr4));
mr4.imr_multiaddr = ((const struct sockaddr_in*) sa)->sin_addr;
r = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr4, sizeof(mr4));
+#ifdef HAVE_IPV6
} else {
struct ipv6_mreq mr6;
memset(&mr6, 0, sizeof(mr6));
mr6.ipv6mr_multiaddr = ((const struct sockaddr_in6*) sa)->sin6_addr;
r = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mr6, sizeof(mr6));
+#endif
}
if (r < 0) {
@@ -560,7 +562,7 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event
} else {
if (!(s = pa_hashmap_get(u->by_origin, info.origin))) {
- if (!(s = session_new(u, &info)))
+ if (!session_new(u, &info))
pa_sdp_info_destroy(&info);
} else {
@@ -608,7 +610,9 @@ int pa__init(pa_module*m) {
struct userdata *u;
pa_modargs *ma = NULL;
struct sockaddr_in sa4;
+#ifdef HAVE_IPV6
struct sockaddr_in6 sa6;
+#endif
struct sockaddr *sa;
socklen_t salen;
const char *sap_address;
@@ -624,16 +628,18 @@ int pa__init(pa_module*m) {
sap_address = pa_modargs_get_value(ma, "sap_address", DEFAULT_SAP_ADDRESS);
- if (inet_pton(AF_INET6, sap_address, &sa6.sin6_addr) > 0) {
- sa6.sin6_family = AF_INET6;
- sa6.sin6_port = htons(SAP_PORT);
- sa = (struct sockaddr*) &sa6;
- salen = sizeof(sa6);
- } else if (inet_pton(AF_INET, sap_address, &sa4.sin_addr) > 0) {
+ if (inet_pton(AF_INET, sap_address, &sa4.sin_addr) > 0) {
sa4.sin_family = AF_INET;
sa4.sin_port = htons(SAP_PORT);
sa = (struct sockaddr*) &sa4;
salen = sizeof(sa4);
+#ifdef HAVE_IPV6
+ } else if (inet_pton(AF_INET6, sap_address, &sa6.sin6_addr) > 0) {
+ sa6.sin6_family = AF_INET6;
+ sa6.sin6_port = htons(SAP_PORT);
+ sa = (struct sockaddr*) &sa6;
+ salen = sizeof(sa6);
+#endif
} else {
pa_log("Invalid SAP address '%s'", sap_address);
goto fail;
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
index 762cdc1e..fef745a1 100644
--- a/src/modules/rtp/module-rtp-send.c
+++ b/src/modules/rtp/module-rtp-send.c
@@ -177,7 +177,9 @@ int pa__init(pa_module*m) {
pa_sample_spec ss;
pa_channel_map cm;
struct sockaddr_in sa4, sap_sa4;
+#ifdef HAVE_IPV6
struct sockaddr_in6 sa6, sap_sa6;
+#endif
struct sockaddr_storage sa_dst;
pa_source_output *o = NULL;
uint8_t payload;
@@ -247,16 +249,18 @@ int pa__init(pa_module*m) {
dest = pa_modargs_get_value(ma, "destination", DEFAULT_DESTINATION);
- if (inet_pton(AF_INET6, dest, &sa6.sin6_addr) > 0) {
- sa6.sin6_family = af = AF_INET6;
- sa6.sin6_port = htons((uint16_t) port);
- sap_sa6 = sa6;
- sap_sa6.sin6_port = htons(SAP_PORT);
- } else if (inet_pton(AF_INET, dest, &sa4.sin_addr) > 0) {
+ if (inet_pton(AF_INET, dest, &sa4.sin_addr) > 0) {
sa4.sin_family = af = AF_INET;
sa4.sin_port = htons((uint16_t) port);
sap_sa4 = sa4;
sap_sa4.sin_port = htons(SAP_PORT);
+#ifdef HAVE_IPV6
+ } else if (inet_pton(AF_INET6, dest, &sa6.sin6_addr) > 0) {
+ sa6.sin6_family = af = AF_INET6;
+ sa6.sin6_port = htons((uint16_t) port);
+ sap_sa6 = sa6;
+ sap_sa6.sin6_port = htons(SAP_PORT);
+#endif
} else {
pa_log("Invalid destination '%s'", dest);
goto fail;
@@ -267,9 +271,14 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (connect(fd, af == AF_INET ? (struct sockaddr*) &sa4 : (struct sockaddr*) &sa6, (socklen_t) (af == AF_INET ? sizeof(sa4) : sizeof(sa6))) < 0) {
+ if (af == AF_INET && connect(fd, (struct sockaddr*) &sa4, sizeof(sa4)) < 0) {
pa_log("connect() failed: %s", pa_cstrerror(errno));
goto fail;
+#ifdef HAVE_IPV6
+ } else if (af == AF_INET6 && connect(fd, (struct sockaddr*) &sa6, sizeof(sa6)) < 0) {
+ pa_log("connect() failed: %s", pa_cstrerror(errno));
+ goto fail;
+#endif
}
if ((sap_fd = socket(af, SOCK_DGRAM, 0)) < 0) {
@@ -277,9 +286,14 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (connect(sap_fd, af == AF_INET ? (struct sockaddr*) &sap_sa4 : (struct sockaddr*) &sap_sa6, (socklen_t) (af == AF_INET ? sizeof(sap_sa4) : sizeof(sap_sa6))) < 0) {
+ if (af == AF_INET && connect(sap_fd, (struct sockaddr*) &sap_sa4, sizeof(sap_sa4)) < 0) {
+ pa_log("connect() failed: %s", pa_cstrerror(errno));
+ goto fail;
+#ifdef HAVE_IPV6
+ } else if (af == AF_INET6 && connect(sap_fd, (struct sockaddr*) &sap_sa6, sizeof(sap_sa6)) < 0) {
pa_log("connect() failed: %s", pa_cstrerror(errno));
goto fail;
+#endif
}
j = !!loop;
@@ -357,10 +371,19 @@ int pa__init(pa_module*m) {
n = pa_sprintf_malloc("PulseAudio RTP Stream on %s", pa_get_fqdn(hn, sizeof(hn)));
- p = pa_sdp_build(af,
- af == AF_INET ? (void*) &((struct sockaddr_in*) &sa_dst)->sin_addr : (void*) &((struct sockaddr_in6*) &sa_dst)->sin6_addr,
- af == AF_INET ? (void*) &sa4.sin_addr : (void*) &sa6.sin6_addr,
+ if (af == AF_INET) {
+ p = pa_sdp_build(af,
+ (void*) &((struct sockaddr_in*) &sa_dst)->sin_addr,
+ (void*) &sa4.sin_addr,
+ n, (uint16_t) port, payload, &ss);
+#ifdef HAVE_IPV6
+ } else {
+ p = pa_sdp_build(af,
+ (void*) &((struct sockaddr_in6*) &sa_dst)->sin6_addr,
+ (void*) &sa6.sin6_addr,
n, (uint16_t) port, payload, &ss);
+#endif
+ }
pa_xfree(n);
diff --git a/src/modules/rtp/sap.c b/src/modules/rtp/sap.c
index 7764f7bd..b5d9df62 100644
--- a/src/modules/rtp/sap.c
+++ b/src/modules/rtp/sap.c
@@ -87,18 +87,31 @@ int pa_sap_send(pa_sap_context *c, pa_bool_t goodbye) {
return -1;
}
+#ifdef HAVE_IPV6
pa_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
+#else
+ pa_assert(sa->sa_family == AF_INET);
+#endif
header = htonl(((uint32_t) 1 << 29) |
+#ifdef HAVE_IPV6
(sa->sa_family == AF_INET6 ? (uint32_t) 1 << 28 : 0) |
+#endif
(goodbye ? (uint32_t) 1 << 26 : 0) |
(c->msg_id_hash));
iov[0].iov_base = &header;
iov[0].iov_len = sizeof(header);
- iov[1].iov_base = sa->sa_family == AF_INET ? (void*) &((struct sockaddr_in*) sa)->sin_addr : (void*) &((struct sockaddr_in6*) sa)->sin6_addr;
- iov[1].iov_len = sa->sa_family == AF_INET ? 4U : 16U;
+ if (sa->sa_family == AF_INET) {
+ iov[1].iov_base = (void*) &((struct sockaddr_in*) sa)->sin_addr;
+ iov[1].iov_len = 4U;
+#ifdef HAVE_IPV6
+ } else {
+ iov[1].iov_base = (void*) &((struct sockaddr_in6*) sa)->sin6_addr;
+ iov[1].iov_len = 16U;
+#endif
+ }
iov[2].iov_base = (char*) MIME_TYPE;
iov[2].iov_len = sizeof(MIME_TYPE);
diff --git a/src/modules/rtp/sdp.c b/src/modules/rtp/sdp.c
index 59989e1a..7c547430 100644
--- a/src/modules/rtp/sdp.c
+++ b/src/modules/rtp/sdp.c
@@ -44,11 +44,16 @@
char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, uint16_t port, uint8_t payload, const pa_sample_spec *ss) {
uint32_t ntp;
char buf_src[64], buf_dst[64], un[64];
- const char *u, *f, *a;
+ const char *u, *f;
pa_assert(src);
pa_assert(dst);
+
+#ifdef HAVE_IPV6
pa_assert(af == AF_INET || af == AF_INET6);
+#else
+ pa_assert(af == AF_INET);
+#endif
pa_assert_se(f = pa_rtp_format_to_string(ss->format));
@@ -57,8 +62,8 @@ char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, u
ntp = (uint32_t) time(NULL) + 2208988800U;
- pa_assert_se(a = inet_ntop(af, src, buf_src, sizeof(buf_src)));
- pa_assert_se(a = inet_ntop(af, dst, buf_dst, sizeof(buf_dst)));
+ pa_assert_se(inet_ntop(af, src, buf_src, sizeof(buf_src)));
+ pa_assert_se(inet_ntop(af, dst, buf_dst, sizeof(buf_dst)));
return pa_sprintf_malloc(
PA_SDP_HEADER
@@ -162,6 +167,7 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
((struct sockaddr_in*) &i->sa)->sin_family = AF_INET;
((struct sockaddr_in*) &i->sa)->sin_port = 0;
i->salen = sizeof(struct sockaddr_in);
+#ifdef HAVE_IPV6
} else if (pa_startswith(t, "c=IN IP6 ")) {
char a[64];
size_t k;
@@ -179,6 +185,7 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
((struct sockaddr_in6*) &i->sa)->sin6_family = AF_INET6;
((struct sockaddr_in6*) &i->sa)->sin6_port = 0;
i->salen = sizeof(struct sockaddr_in6);
+#endif
} else if (pa_startswith(t, "m=audio ")) {
if (i->payload > 127) {
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c
index 983b8977..82e36c00 100644
--- a/src/pulse/channelmap.c
+++ b/src/pulse/channelmap.c
@@ -217,10 +217,10 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
case 6:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
- m->map[1] = PA_CHANNEL_POSITION_SIDE_LEFT;
+ m->map[1] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT;
- m->map[4] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+ m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
m->map[5] = PA_CHANNEL_POSITION_LFE;
return m;
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 81050914..9309c6b7 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -129,9 +129,6 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *
pa_init_i18n();
- if (!name && !pa_proplist_contains(p, PA_PROP_APPLICATION_NAME))
- return NULL;
-
c = pa_xnew(pa_context, 1);
PA_REFCNT_INIT(c);
@@ -338,8 +335,7 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
pa_assert(p);
pa_assert(chunk);
- pa_assert(chunk->memblock);
- pa_assert(chunk->length);
+ pa_assert(chunk->length > 0);
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
@@ -347,11 +343,11 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
if ((s = pa_dynarray_get(c->record_streams, channel))) {
- pa_assert(seek == PA_SEEK_RELATIVE);
- pa_assert(offset == 0);
-
- pa_memblockq_seek(s->record_memblockq, offset, seek);
- pa_memblockq_push_align(s->record_memblockq, chunk);
+ if (chunk->memblock) {
+ pa_memblockq_seek(s->record_memblockq, offset, seek);
+ pa_memblockq_push_align(s->record_memblockq, chunk);
+ } else
+ pa_memblockq_seek(s->record_memblockq, offset+chunk->length, seek);
if (s->read_callback) {
size_t l;
@@ -558,6 +554,7 @@ static void setup_context(pa_context *c, pa_iochannel *io) {
pa_context_unref(c);
}
+#if ENABLE_LEGACY_RUNTIME_DIR
static char *get_old_legacy_runtime_dir(void) {
char *p, u[128];
struct stat st;
@@ -601,10 +598,12 @@ static char *get_very_old_legacy_runtime_dir(void) {
return p;
}
-
+#endif
static pa_strlist *prepend_per_user(pa_strlist *l) {
char *ufn;
+
+#if ENABLE_LEGACY_RUNTIME_DIR
static char *legacy_dir;
/* The very old per-user instance path (< 0.9.11). This is supported only to ease upgrades */
@@ -622,6 +621,7 @@ static pa_strlist *prepend_per_user(pa_strlist *l) {
pa_xfree(p);
pa_xfree(legacy_dir);
}
+#endif
/* The per-user instance */
if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) {
diff --git a/src/pulse/gccmacro.h b/src/pulse/gccmacro.h
index 0b1a1a66..58188ee2 100644
--- a/src/pulse/gccmacro.h
+++ b/src/pulse/gccmacro.h
@@ -88,7 +88,7 @@
#endif
#ifndef PA_GCC_PACKED
-#ifdef __GNUCC__
+#ifdef __GNUC__
#define PA_GCC_PACKED __attribute__ ((packed))
#else
/** Structure shall be packed in memory **/
@@ -109,7 +109,7 @@
#endif
#ifndef PA_GCC_MALLOC
-#ifdef __GNUCC__
+#ifdef __GNUC__
#define PA_GCC_MALLOC __attribute__ ((malloc))
#else
/** Macro for usage of GCC's malloc attribute */
@@ -117,4 +117,11 @@
#endif
#endif
+#ifndef PA_GCC_WEAKREF
+#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ > 1)) || (__GNUC__ > 4))
+/** Macro for usgae of GCC's weakref attribute */
+#define PA_GCC_WEAKREF(x) __attribute__((weakref(#x)));
+#endif
+#endif
+
#endif
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 1d50939c..04bcd4f5 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -162,6 +162,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
i.n_volume_steps = PA_VOLUME_NORM+1;
mute = FALSE;
state = PA_SINK_INVALID_STATE;
+ i.card = PA_INVALID_INDEX;
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -182,7 +183,8 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
(o->context->version >= 15 &&
(pa_tagstruct_get_volume(t, &i.base_volume) < 0 ||
pa_tagstruct_getu32(t, &state) < 0 ||
- pa_tagstruct_getu32(t, &i.n_volume_steps) < 0))) {
+ pa_tagstruct_getu32(t, &i.n_volume_steps) < 0 ||
+ pa_tagstruct_getu32(t, &i.card) < 0))) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_proplist_free(i.proplist);
@@ -293,6 +295,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
i.n_volume_steps = PA_VOLUME_NORM+1;
mute = FALSE;
state = PA_SOURCE_INVALID_STATE;
+ i.card = PA_INVALID_INDEX;
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -313,7 +316,8 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
(o->context->version >= 15 &&
(pa_tagstruct_get_volume(t, &i.base_volume) < 0 ||
pa_tagstruct_getu32(t, &state) < 0 ||
- pa_tagstruct_getu32(t, &i.n_volume_steps) < 0))) {
+ pa_tagstruct_getu32(t, &i.n_volume_steps) < 0 ||
+ pa_tagstruct_getu32(t, &i.card) < 0))) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_proplist_free(i.proplist);
@@ -517,7 +521,9 @@ static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, u
for (j = 0; j < i.n_profiles; j++) {
if (pa_tagstruct_gets(t, &i.profiles[j].name) < 0 ||
- pa_tagstruct_gets(t, &i.profiles[j].description) < 0) {
+ pa_tagstruct_gets(t, &i.profiles[j].description) < 0 ||
+ pa_tagstruct_getu32(t, &i.profiles[j].n_sinks) < 0 ||
+ pa_tagstruct_getu32(t, &i.profiles[j].n_sources)< 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.profiles);
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index badc787e..b873a84a 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -215,6 +215,7 @@ typedef struct pa_sink_info {
pa_volume_t base_volume; /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the output device. \since 0.9.15 */
pa_sink_state_t state; /**< State \since 0.9.15 */
uint32_t n_volume_steps; /**< Number of volume steps for sinks which do not support arbitrary volumes. \since 0.9.15 */
+ uint32_t card; /**< Card index, or PA_INVALID_INDEX. \since 0.9.15 */
} pa_sink_info;
/** Callback prototype for pa_context_get_sink_info_by_name() and friends */
@@ -273,6 +274,7 @@ typedef struct pa_source_info {
pa_volume_t base_volume; /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the input device. \since 0.9.15 */
pa_source_state_t state; /**< State \since 0.9.15 */
uint32_t n_volume_steps; /**< Number of volume steps for sources which do not support arbitrary volumes. \since 0.9.15 */
+ uint32_t card; /**< Card index, or PA_INVALID_INDEX. \since 0.9.15 */
} pa_source_info;
/** Callback prototype for pa_context_get_source_info_by_name() and friends */
@@ -396,6 +398,8 @@ pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_suc
typedef struct pa_card_profile_info {
const char *name; /**< Name of this profile */
const char *description; /**< Description of this profile */
+ uint32_t n_sinks; /**< Number of sinks this profile would create */
+ uint32_t n_sources; /**< Number of sources this profile would create */
} pa_card_profile_info;
/** Stores information about cards. Please note that this structure
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 57a23d9f..c0c34593 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -30,110 +30,163 @@
PA_C_DECL_BEGIN
-/* Defined properties:
- *
- * media.name "Guns'N'Roses: Civil War"
- * media.title "Civil War"
- * media.artist "Guns'N'Roses"
- * media.language "de_DE"
- * media.filename
- * media.icon Binary blob containing PNG icon data
- * media.icon_name Name from XDG icon naming spec
- * media.role video, music, game, event, phone, animation, production, filter, abstract, stream
- * event.id Name from XDG sound naming spec
- * event.description "Button blabla clicked" for a11y
- * event.mouse.x
- * event.mouse.y
- * event.mouse.hpos Float formatted as string in range 0..1
- * event.mouse.vpos Float formatted as string in range 0..1
- * event.mouse.button Button number following X11 ordering
- * window.name
- * window.id "org.gnome.rhytmbox.MainWindow"
- * window.icon Binary blob containing PNG icon data
- * window.icon_name Name from XDG icon naming spec
- * window.x11.display
- * window.x11.screen
- * window.x11.monitor
- * window.x11.xid
- * application.name "Rhythmbox Media Player"
- * application.id "org.gnome.rhythmbox"
- * application.version
- * application.icon Binary blob containing PNG icon data
- * application.icon_name Name from XDG icon naming spec
- * application.language
- * application.process.id
- * application.process.binary
- * application.process.user
- * application.process.host
- * application.process.machine_id D-Bus machine ID
- * device.string
- * device.api oss, alsa, sunaudio
- * device.description
- * device.bus_path
- * device.serial
- * device.vendor_product_id
- * device.class sound, modem, monitor, filter, abstract
- * device.form_factor laptop-speakers, external-speakers, telephone, tv-capture, webcam-capture, microphone-capture, headset, headphones, hands-free, car, hifi, computer, portable
- * device.connector isa, pci, usb, firewire, bluetooth
- * device.access_mode mmap, mmap_rewrite, serial
- * device.master_device
- * device.buffering.buffer_size
- * device.buffering.fragment_size
- * device.profile.name analog-stereo, analog-surround-40, iec958-stereo, ...
- * device.profile.description "Analog Stereo", ...
- */
+/** For streams: localized media name, formatted as UTF-8. e.g. "Guns'N'Roses: Civil War".*/
#define PA_PROP_MEDIA_NAME "media.name"
+
+/** For streams: localized media title if applicable, formatted as UTF-8. e.g. "Civil War" */
#define PA_PROP_MEDIA_TITLE "media.title"
+
+/** For streams: localized media artist if applicable, formatted as UTF-8. e.g. "Guns'N'Roses" */
#define PA_PROP_MEDIA_ARTIST "media.artist"
+
+/** For streams: media language if applicable, in standard POSIX format. e.g. "de_DE" */
#define PA_PROP_MEDIA_LANGUAGE "media.language"
+
+/** For streams: source filename if applicable, in URI format or local path. e.g. "/home/lennart/music/foobar.ogg" */
#define PA_PROP_MEDIA_FILENAME "media.filename"
+
+/** For streams: icon for the media. A binary blob containing PNG image data */
#define PA_PROP_MEDIA_ICON "media.icon"
+
+/** For streams: an XDG icon name for the media. e.g. "audio-x-mp3" */
#define PA_PROP_MEDIA_ICON_NAME "media.icon_name"
+
+/** For streams: logic role of this media. One of the strings "video", "music", "game", "event", "phone", "animation", "production" */
#define PA_PROP_MEDIA_ROLE "media.role"
+
+/** For event sound streams: XDG event sound name. e.g. "message-new-email" (Event sound streams are those with media.role set to "event") */
#define PA_PROP_EVENT_ID "event.id"
+
+/** For event sound streams: localized human readable one-line description of the event, formatted as UTF-8. e.g. "Email from lennart@example.com received." */
#define PA_PROP_EVENT_DESCRIPTION "event.description"
+
+/** For event sound streams: absolute horizontal mouse position on the screen if the event sound was triggered by a mouse click, integer formatted as text string. e.g. "865" */
#define PA_PROP_EVENT_MOUSE_X "event.mouse.x"
+
+/** For event sound streams: absolute vertical mouse position on the screen if the event sound was triggered by a mouse click, integer formatted as text string. e.g. "432" */
#define PA_PROP_EVENT_MOUSE_Y "event.mouse.y"
+
+/** For event sound streams: relative horizontal mouse position on the screen if the event sound was triggered by a mouse click, float formatted as text string, ranging from 0.0 (left side of the screen) to 1.0 (right side of the screen). e.g. "0.65" */
#define PA_PROP_EVENT_MOUSE_HPOS "event.mouse.hpos"
+
+/** For event sound streams: relative vertical mouse position on the screen if the event sound was triggered by a mouse click, float formatted as text string, ranging from 0.0 (top of the screen) to 1.0 (bottom of the screen). e.g. "0.43" */
#define PA_PROP_EVENT_MOUSE_VPOS "event.mouse.vpos"
+
+/** For event sound streams: mouse button that triggered the event if applicable, integer formatted as string with 0=left, 1=middle, 2=right. e.g. "0" */
#define PA_PROP_EVENT_MOUSE_BUTTON "event.mouse.button"
+
+/** For streams that belong to a window on the screen: localized window title. e.g. "Totem Music Player" */
#define PA_PROP_WINDOW_NAME "window.name"
+
+/** For streams that belong to a window on the screen: a textual id for identifying a window logically. e.g. "org.gnome.Totem.MainWindow" */
#define PA_PROP_WINDOW_ID "window.id"
+
+/** For streams that belong to a window on the screen: window icon. A binary blob containing PNG image data */
#define PA_PROP_WINDOW_ICON "window.icon"
+
+/** For streams that belong to a window on the screen: an XDG icon name for the window. e.g. "totem" */
#define PA_PROP_WINDOW_ICON_NAME "window.icon_name"
+
+/** For streams that belong to an X11 window on the screen: the X11 display string. e.g. ":0.0" */
#define PA_PROP_WINDOW_X11_DISPLAY "window.x11.display"
+
+/** For streams that belong to an X11 window on the screen: the X11 screen the window is on, an integer formatted as string. e.g. "0" */
#define PA_PROP_WINDOW_X11_SCREEN "window.x11.screen"
+
+/** For streams that belong to an X11 window on the screen: the X11 monitor the window is on, an integer formatted as string. e.g. "0" */
#define PA_PROP_WINDOW_X11_MONITOR "window.x11.monitor"
+
+/** For streams that belong to an X11 window on the screen: the window XID, an integer formatted as string. e.g. "25632" */
#define PA_PROP_WINDOW_X11_XID "window.x11.xid"
+
+/** For clients/streams: localized human readable application name. e.g. "Totem Music Player" */
#define PA_PROP_APPLICATION_NAME "application.name"
+
+/** For clients/streams: a textual id for identifying an application logically. e.g. "org.gnome.Totem" */
#define PA_PROP_APPLICATION_ID "application.id"
+
+/** For clients/streams: a version string e.g. "0.6.88" */
#define PA_PROP_APPLICATION_VERSION "application.version"
+
+/** For clients/streams: application icon. A binary blob containing PNG image data */
#define PA_PROP_APPLICATION_ICON "application.icon"
+
+/** For clients/streams: an XDG icon name for the application. e.g. "totem" */
#define PA_PROP_APPLICATION_ICON_NAME "application.icon_name"
+
+/** For clients/streams: application language if applicable, in standard POSIX format. e.g. "de_DE" */
#define PA_PROP_APPLICATION_LANGUAGE "application.language"
+
+/** For clients/streams on UNIX: application process PID, an integer formatted as string. e.g. "4711" */
#define PA_PROP_APPLICATION_PROCESS_ID "application.process.id"
+
+/** For clients/streams: application process name. e.g. "totem" */
#define PA_PROP_APPLICATION_PROCESS_BINARY "application.process.binary"
+
+/** For clients/streams: application user name. e.g. "lennart" */
#define PA_PROP_APPLICATION_PROCESS_USER "application.process.user"
+
+/** For clients/streams: host name the application runs on. e.g. "omega" */
#define PA_PROP_APPLICATION_PROCESS_HOST "application.process.host"
+
+/** For clients/streams: the D-Bus host id the application runs on. e.g. "543679e7b01393ed3e3e650047d78f6e" */
#define PA_PROP_APPLICATION_PROCESS_MACHINE_ID "application.process.machine_id"
+
+/** For devices: device string in the underlying audio layer's format. e.g. "surround51:0" */
#define PA_PROP_DEVICE_STRING "device.string"
+
+/** For devices: API this device is access with. e.g. "alsa" */
#define PA_PROP_DEVICE_API "device.api"
+
+/** For devices: localized human readable device one-line description, e.g. "Foobar Industries USB Headset 2000+ Ultra" */
#define PA_PROP_DEVICE_DESCRIPTION "device.description"
+
+/** For devices: bus path to the device in the OS' format. e.g. "/sys/bus/pci/devices/0000:00:1f.2" */
#define PA_PROP_DEVICE_BUS_PATH "device.bus_path"
+
+/** For devices: serial number if applicable. e.g. "4711-0815-1234" */
#define PA_PROP_DEVICE_SERIAL "device.serial"
+
+/** For devices: vendor/product ID if applicable. e.g. 1274:1371 */
#define PA_PROP_DEVICE_VENDOR_PRODUCT_ID "device.vendor_product_id"
+
+/** For devices: device class. One of "sound", "modem", "monitor", "filter" */
#define PA_PROP_DEVICE_CLASS "device.class"
+
+/** For devices: form factor if applicable. One of "laptop-speakers", "external-speakers", "telephone", "tv-capture", "webcam-capture", "microphone-capture", "headset", "headphones", "hands-free", "car", "hifi", "computer", "portable" */
#define PA_PROP_DEVICE_FORM_FACTOR "device.form_factor"
+
+/** For devices: connector of the device if applicable. One of "isa", "pci", "usb", "firewire", "bluetooth" */
#define PA_PROP_DEVICE_CONNECTOR "device.connector"
+
+/** For devices: access mode of the device if applicable. One of "mmap", "mmap_rewrite", "serial" */
#define PA_PROP_DEVICE_ACCESS_MODE "device.access_mode"
+
+/** For filter devices: master device id if applicable. */
#define PA_PROP_DEVICE_MASTER_DEVICE "device.master_device"
+
+/** For devices: buffer size in bytes, integer formatted as string.. */
#define PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE "device.buffering.buffer_size"
+
+/** For devices: fragment size in bytes, integer formatted as string. */
#define PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE "device.buffering.fragment_size"
+
+/** For devices: profile identifier for the profile this devices is in. e.g. "analog-stereo", "analog-surround-40", "iec958-stereo", ...*/
#define PA_PROP_DEVICE_PROFILE_NAME "device.profile.name"
+
+/** For devices: human readable one-line description of the profile this device is in. e.g. "Analog Stereo", ... */
#define PA_PROP_DEVICE_PROFILE_DESCRIPTION "device.profile.description"
+
+/** For modules: the author's name, formatted as UTF-8 string. e.g. "Lennart Poettering" */
#define PA_PROP_MODULE_AUTHOR "module.author"
+
+/** For modules: a human readable one-line description of the module's purpose formatted as UTF-8. e.g. "Frobnicate sounds with a flux compensator" */
#define PA_PROP_MODULE_DESCRIPTION "module.description"
+
+/** For modules: a human readable usage description of the module's arguments formatted as UTF-8. */
#define PA_PROP_MODULE_USAGE "module.usage"
+
+/** For modules: a version string for the module. e.g. "0.9.15" */
#define PA_PROP_MODULE_VERSION "module.version"
/** A property list object. Basically a dictionary with ASCII strings
@@ -179,17 +232,17 @@ int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *
/** Update mode enum for pa_proplist_update(). \since 0.9.11 */
typedef enum pa_update_mode {
- PA_UPDATE_SET,
- /*< Replace the entirey property list with the new one. Don't keep
- * any of the old data around */
+ PA_UPDATE_SET
+ /**< Replace the entirey property list with the new one. Don't keep
+ * any of the old data around */,
- PA_UPDATE_MERGE,
- /*< Merge new property list into the existing one, not replacing
+ PA_UPDATE_MERGE
+ /**< Merge new property list into the existing one, not replacing
* any old entries if they share a common key with the new
- * property list. */
+ * property list. */,
PA_UPDATE_REPLACE
- /*< Merge new property list into the existing one, replacing all
+ /**< Merge new property list into the existing one, replacing all
* old entries that share a common key with the new property
* list. */
} pa_update_mode_t;
diff --git a/src/pulse/scache.c b/src/pulse/scache.c
index c96c42ad..a7e3cd81 100644
--- a/src/pulse/scache.c
+++ b/src/pulse/scache.c
@@ -66,10 +66,8 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) {
pa_tagstruct_put_channel_map(t, &s->channel_map);
pa_tagstruct_putu32(t, (uint32_t) length);
- if (s->context->version >= 13) {
- pa_init_proplist(s->proplist);
+ if (s->context->version >= 13)
pa_tagstruct_put_proplist(t, s->proplist);
- }
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
diff --git a/src/pulse/util.c b/src/pulse/util.c
index b20ea46a..54a188d5 100644
--- a/src/pulse/util.c
+++ b/src/pulse/util.c
@@ -54,6 +54,8 @@
#endif
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
#include <pulsecore/winsock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
@@ -260,8 +262,8 @@ int pa_msleep(unsigned long t) {
#elif defined(HAVE_NANOSLEEP)
struct timespec ts;
- ts.tv_sec = (time_t) (t/1000UL);
- ts.tv_nsec = (long) ((t % 1000UL) * 1000000UL);
+ ts.tv_sec = (time_t) (t / PA_MSEC_PER_SEC);
+ ts.tv_nsec = (long) ((t % PA_MSEC_PER_SEC) * PA_NSEC_PER_MSEC);
return nanosleep(&ts, NULL);
#else
diff --git a/src/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c
index 5c7af2a8..e191b05f 100644
--- a/src/pulsecore/asyncmsgq.c
+++ b/src/pulsecore/asyncmsgq.c
@@ -159,7 +159,7 @@ int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const voi
pa_assert_se(i.semaphore);
- /* Thus mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
+ /* This mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
pa_mutex_lock(a->mutex);
pa_assert_se(pa_asyncq_push(a->asyncq, &i, TRUE) == 0);
pa_mutex_unlock(a->mutex);
@@ -196,7 +196,13 @@ int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **u
if (chunk)
*chunk = a->current->memchunk;
-/* pa_log_debug("Get q=%p object=%p (%s) code=%i data=%p chunk.length=%lu", (void*) a, (void*) a->current->object, a->current->object ? a->current->object->parent.type_name : NULL, a->current->code, (void*) a->current->userdata, (unsigned long) a->current->memchunk.length); */
+/* pa_log_debug("Get q=%p object=%p (%s) code=%i data=%p chunk.length=%lu", */
+/* (void*) a, */
+/* (void*) a->current->object, */
+/* a->current->object ? a->current->object->parent.type_name : NULL, */
+/* a->current->code, */
+/* (void*) a->current->userdata, */
+/* (unsigned long) a->current->memchunk.length); */
return 0;
}
diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index 8e1ba536..515d1f90 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -229,7 +229,7 @@ int pa_card_set_profile(pa_card *c, const char *name) {
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
- pa_log_info("Successfully changed profile of card %u \"%s\" to %s", c->index, c->name, profile->name);
+ pa_log_info("Changed profile of card %u \"%s\" to %s", c->index, c->name, profile->name);
c->active_profile = profile;
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 1df0bd63..5e45c1aa 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -401,7 +401,6 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
}
static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
- pa_module *m;
const char *name;
pa_core_assert_ref(c);
@@ -414,7 +413,7 @@ static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
return -1;
}
- if (!(m = pa_module_load(c, name, pa_tokenizer_get(t, 2)))) {
+ if (!pa_module_load(c, name, pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "Module load failed.\n");
return -1;
}
diff --git a/src/pulsecore/core-error.c b/src/pulsecore/core-error.c
index 3d6c2c3b..d9caa946 100644
--- a/src/pulsecore/core-error.c
+++ b/src/pulsecore/core-error.c
@@ -47,6 +47,9 @@ const char* pa_cstrerror(int errnum) {
char *translated, *t;
char errbuf[128];
+ if (errnum < 0)
+ errnum = -errnum;
+
if ((t = PA_STATIC_TLS_GET(cstrerror)))
pa_xfree(t);
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index ad6c6ca9..a184bebd 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -704,7 +704,7 @@ void pa_reset_priority(void) {
#endif
}
-static int match(const char *expr, const char *v) {
+int pa_match(const char *expr, const char *v) {
int k;
regex_t re;
int r;
@@ -744,12 +744,12 @@ int pa_parse_boolean(const char *v) {
/* And then we check language dependant */
if ((expr = nl_langinfo(YESEXPR)))
if (expr[0])
- if ((r = match(expr, v)) > 0)
+ if ((r = pa_match(expr, v)) > 0)
return 1;
if ((expr = nl_langinfo(NOEXPR)))
if (expr[0])
- if ((r = match(expr, v)) > 0)
+ if ((r = pa_match(expr, v)) > 0)
return 0;
errno = EINVAL;
@@ -1411,6 +1411,7 @@ static int make_random_dir_and_link(mode_t m, const char *k) {
return -1;
}
+ pa_xfree(p);
return 0;
}
@@ -1443,6 +1444,7 @@ char *pa_get_runtime_dir(void) {
if (pa_make_secure_dir(d, m, (uid_t) -1, (gid_t) -1) < 0) {
pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
+ pa_xfree(d);
goto fail;
}
@@ -2459,7 +2461,7 @@ char *pa_machine_id(void) {
pa_strip_nl(ln);
- if (ln[0])
+ if (r && ln[0])
return pa_xstrdup(ln);
}
@@ -2604,3 +2606,28 @@ char *pa_unescape(char *p) {
return p;
}
+
+char *pa_realpath(const char *path) {
+ char *r, *t;
+ pa_assert(path);
+
+ /* We want only abolsute paths */
+ if (path[0] != '/') {
+ errno = EINVAL;
+ return NULL;
+ }
+
+#ifndef __GLIBC__
+#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
+
+ if (!(r = realpath(path, NULL)))
+ return NULL;
+
+ /* We copy this here in case our pa_xmalloc() is not implemented
+ * on top of libc malloc() */
+ t = pa_xstrdup(r);
+ pa_xfree(r);
+
+ return t;
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 442815f1..0ba33f31 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -141,6 +141,8 @@ size_t pa_vsnprintf(char *str, size_t size, const char *format, va_list ap);
char *pa_truncate_utf8(char *c, size_t l);
+int pa_match(const char *expr, const char *v);
+
char *pa_getcwd(void);
char *pa_make_path_absolute(const char *p);
pa_bool_t pa_is_path_absolute(const char *p);
@@ -219,4 +221,6 @@ char *pa_replace(const char*s, const char*a, const char *b);
char *pa_unescape(char *p);
+char *pa_realpath(const char *path);
+
#endif
diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c
index 3c6f41ec..57607b69 100644
--- a/src/pulsecore/hashmap.c
+++ b/src/pulsecore/hashmap.c
@@ -138,7 +138,7 @@ int pa_hashmap_put(pa_hashmap *h, const void *key, void *value) {
hash = h->hash_func(key) % NBUCKETS;
- if ((e = hash_scan(h, hash, key)))
+ if (hash_scan(h, hash, key))
return -1;
if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
diff --git a/src/pulsecore/inet_ntop.c b/src/pulsecore/inet_ntop.c
index 87551232..012a1a05 100644
--- a/src/pulsecore/inet_ntop.c
+++ b/src/pulsecore/inet_ntop.c
@@ -38,7 +38,9 @@
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
struct in_addr *in = (struct in_addr*)src;
+#ifdef HAVE_IPV6
struct in6_addr *in6 = (struct in6_addr*)src;
+#endif
assert(src && dst);
@@ -57,6 +59,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
(int)(in->s_addr >> 24) & 0xff);
#endif
break;
+#ifdef HAVE_IPV6
case AF_INET6:
pa_snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x",
in6->s6_addr[ 0] << 8 | in6->s6_addr[ 1],
@@ -68,6 +71,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
in6->s6_addr[12] << 8 | in6->s6_addr[13],
in6->s6_addr[14] << 8 | in6->s6_addr[15]);
break;
+#endif
default:
errno = EAFNOSUPPORT;
return NULL;
diff --git a/src/pulsecore/inet_pton.c b/src/pulsecore/inet_pton.c
index d191e550..abdfa467 100644
--- a/src/pulsecore/inet_pton.c
+++ b/src/pulsecore/inet_pton.c
@@ -38,7 +38,9 @@
int inet_pton(int af, const char *src, void *dst) {
struct in_addr *in = (struct in_addr*)dst;
+#ifdef HAVE_IPV6
struct in6_addr *in6 = (struct in6_addr*)dst;
+#endif
assert(src && dst);
@@ -48,8 +50,10 @@ int inet_pton(int af, const char *src, void *dst) {
if (in->s_addr == INADDR_NONE)
return 0;
break;
+#ifdef HAVE_IPV6
case AF_INET6:
/* FIXME */
+#endif
default:
errno = EAFNOSUPPORT;
return -1;
diff --git a/src/pulsecore/ipacl.c b/src/pulsecore/ipacl.c
index 6d5080fb..312e0403 100644
--- a/src/pulsecore/ipacl.c
+++ b/src/pulsecore/ipacl.c
@@ -62,7 +62,9 @@ struct acl_entry {
PA_LLIST_FIELDS(struct acl_entry);
int family;
struct in_addr address_ipv4;
+#ifdef HAVE_IPV6
struct in6_addr address_ipv6;
+#endif
int bits;
};
@@ -109,6 +111,7 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {
if (e.bits < 32 && (uint32_t) (ntohl(e.address_ipv4.s_addr) << e.bits) != 0)
pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
+#ifdef HAVE_IPV6
} else if (inet_pton(AF_INET6, a, &e.address_ipv6) > 0) {
e.bits = bits == (uint32_t) -1 ? 128 : (int) bits;
@@ -138,6 +141,7 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {
if (t)
pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
}
+#endif
} else {
pa_log_warn("Failed to parse address: %s", a);
@@ -183,14 +187,20 @@ int pa_ip_acl_check(pa_ip_acl *acl, int fd) {
if (getpeername(fd, (struct sockaddr*) &sa, &salen) < 0)
return -1;
+#ifdef HAVE_IPV6
if (sa.ss_family != AF_INET && sa.ss_family != AF_INET6)
+#else
+ if (sa.ss_family != AF_INET)
+#endif
return -1;
if (sa.ss_family == AF_INET && salen != sizeof(struct sockaddr_in))
return -1;
+#ifdef HAVE_IPV6
if (sa.ss_family == AF_INET6 && salen != sizeof(struct sockaddr_in6))
return -1;
+#endif
for (e = acl->entries; e; e = e->next) {
@@ -203,6 +213,7 @@ int pa_ip_acl_check(pa_ip_acl *acl, int fd) {
if (e->bits == 0 || /* this needs special handling because >> takes the right-hand side modulo 32 */
(ntohl(sai->sin_addr.s_addr ^ e->address_ipv4.s_addr) >> (32 - e->bits)) == 0)
return 1;
+#ifdef HAVE_IPV6
} else if (e->family == AF_INET6) {
int i, bits ;
struct sockaddr_in6 *sai = (struct sockaddr_in6*) &sa;
@@ -230,6 +241,7 @@ int pa_ip_acl_check(pa_ip_acl *acl, int fd) {
if (bits == 0)
return 1;
}
+#endif
}
}
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index 1ae43839..89b75da3 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -351,6 +351,7 @@ void pa_log_levelv_meta(
}
errno = saved_errno;
+ pa_xfree(bt);
}
void pa_log_level_meta(
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
index 1d7f4559..fbf0a470 100644
--- a/src/pulsecore/memblock.c
+++ b/src/pulsecore/memblock.c
@@ -57,7 +57,7 @@
#define PA_MEMEXPORT_SLOTS_MAX 128
-#define PA_MEMIMPORT_SLOTS_MAX 128
+#define PA_MEMIMPORT_SLOTS_MAX 160
#define PA_MEMIMPORT_SEGMENTS_MAX 16
struct pa_memblock {
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
index 8a447cf7..bdae0e65 100644
--- a/src/pulsecore/proplist-util.c
+++ b/src/pulsecore/proplist-util.c
@@ -34,6 +34,7 @@
extern char **environ;
#endif
+#include <pulse/gccmacro.h>
#include <pulse/proplist.h>
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
@@ -41,8 +42,64 @@ extern char **environ;
#include <pulsecore/core-util.h>
+#if defined(HAVE_GLIB) && defined(PA_GCC_WEAKREF)
+#include <glib.h>
+static G_CONST_RETURN gchar* _g_get_application_name(void) PA_GCC_WEAKREF(g_get_application_name);
+#endif
+
+#if defined(HAVE_GTK) && defined(PA_GCC_WEAKREF)
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+static G_CONST_RETURN gchar* _gtk_window_get_default_icon_name(void) PA_GCC_WEAKREF(gtk_window_get_default_icon_name);
+static Display *_gdk_display PA_GCC_WEAKREF(gdk_display);
+#endif
+
#include "proplist-util.h"
+static void add_glib_properties(pa_proplist *p) {
+
+#if defined(HAVE_GLIB) && defined(PA_GCC_WEAKREF)
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_NAME))
+ if (_g_get_application_name) {
+ const gchar *t;
+
+ /* We ignore the tiny race condition here. */
+
+ if ((t = _g_get_application_name()))
+ pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, t);
+ }
+
+#endif
+}
+
+static void add_gtk_properties(pa_proplist *p) {
+
+#if defined(HAVE_GTK) && defined(PA_GCC_WEAKREF)
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_ICON_NAME))
+ if (_gtk_window_get_default_icon_name) {
+ const gchar *t;
+
+ /* We ignore the tiny race condition here. */
+
+ if ((t = _gtk_window_get_default_icon_name()))
+ pa_proplist_sets(p, PA_PROP_APPLICATION_ICON_NAME, t);
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_WINDOW_X11_DISPLAY))
+ if (&_gdk_display && _gdk_display) {
+ const char *t;
+
+ /* We ignore the tiny race condition here. */
+
+ if ((t = DisplayString(_gdk_display)))
+ pa_proplist_sets(p, PA_PROP_WINDOW_X11_DISPLAY, t);
+ }
+
+#endif
+}
+
void pa_init_proplist(pa_proplist *p) {
char **e;
const char *pp;
@@ -58,18 +115,27 @@ void pa_init_proplist(pa_proplist *p) {
for (e = environ; *e; e++) {
if (pa_startswith(*e, "PULSE_PROP_")) {
- size_t kl = strcspn(*e+11, "=");
+ size_t kl, skip;
char *k;
+ pa_bool_t override;
- if ((*e)[11+kl] != '=')
- continue;
+ if (pa_startswith(*e, "PULSE_PROP_OVERRIDE_")) {
+ skip = 20;
+ override = TRUE;
+ } else {
+ skip = 11;
+ override = FALSE;
+ }
- if (!pa_utf8_valid(*e+11+kl+1))
+ kl = strcspn(*e+skip, "=");
+
+ if ((*e)[skip+kl] != '=')
continue;
- k = pa_xstrndup(*e+11, kl);
+ k = pa_xstrndup(*e+skip, kl);
- pa_proplist_sets(p, k, *e+11+kl+1);
+ if (override || !pa_proplist_contains(p, k))
+ pa_proplist_sets(p, k, *e+skip+kl+1);
pa_xfree(k);
}
}
@@ -79,6 +145,15 @@ void pa_init_proplist(pa_proplist *p) {
pa_proplist *t;
if ((t = pa_proplist_from_string(pp))) {
+ pa_proplist_update(p, PA_UPDATE_MERGE, t);
+ pa_proplist_free(t);
+ }
+ }
+
+ if ((pp = getenv("PULSE_PROP_OVERRIDE"))) {
+ pa_proplist *t;
+
+ if ((t = pa_proplist_from_string(pp))) {
pa_proplist_update(p, PA_UPDATE_REPLACE, t);
pa_proplist_free(t);
}
@@ -117,20 +192,8 @@ void pa_init_proplist(pa_proplist *p) {
}
}
-#ifdef RTLD_NOLOAD
- if (!pa_proplist_contains(p, PA_PROP_APPLICATION_NAME)) {
- void *dl;
-
- if ((dl = dlopen("libglib-2.0", RTLD_NOLOAD))) {
- const char *(*_g_get_application_name)(void);
-
- if ((*(void**) &_g_get_application_name = dlsym(dl, "g_get_application_name")))
- pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, _g_get_application_name());
-
- dlclose(dl);
- }
- }
-#endif
+ add_glib_properties(p);
+ add_gtk_properties(p);
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_NAME)) {
const char *t;
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index e1643cbb..840f4581 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -924,7 +924,7 @@ static int do_read(connection *c) {
c->request = PA_MAYBE_INT32_SWAP(c->swap_byte_order, c->request);
- if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) {
+ if (c->request < ESD_PROTO_CONNECT || c->request >= ESD_PROTO_MAX) {
pa_log("recieved invalid request.");
return -1;
}
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index c3032618..a963f78a 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -2560,7 +2560,10 @@ static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uin
if (!(name = pa_proplist_gets(p, PA_PROP_EVENT_ID)))
name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME);
- CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+ if (!name || !pa_namereg_is_valid_name(name)) {
+ pa_proplist_free(p);
+ CHECK_VALIDITY(c->pstream, FALSE, tag, PA_ERR_INVALID);
+ }
s = upload_stream_new(c, &ss, &map, name, length, p);
pa_proplist_free(p);
@@ -2749,6 +2752,7 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
pa_log_error("Internal sink state is invalid.");
pa_tagstruct_putu32(t, pa_sink_get_state(sink));
pa_tagstruct_putu32(t, sink->n_volume_steps);
+ pa_tagstruct_putu32(t, sink->card ? sink->card->index : PA_INVALID_INDEX);
}
}
@@ -2788,6 +2792,7 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
pa_log_error("Internal source state is invalid.");
pa_tagstruct_putu32(t, pa_source_get_state(source));
pa_tagstruct_putu32(t, source->n_volume_steps);
+ pa_tagstruct_putu32(t, source->card ? source->card->index : PA_INVALID_INDEX);
}
}
@@ -2822,6 +2827,8 @@ static void card_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_car
while ((p = pa_hashmap_iterate(card->profiles, &state, NULL))) {
pa_tagstruct_puts(t, p->name);
pa_tagstruct_puts(t, p->description);
+ pa_tagstruct_putu32(t, p->n_sinks);
+ pa_tagstruct_putu32(t, p->n_sources);
}
}
@@ -3007,7 +3014,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
source_fill_tagstruct(c, reply, source);
else if (client)
client_fill_tagstruct(c, reply, client);
- else if (client)
+ else if (card)
card_fill_tagstruct(c, reply, card);
else if (module)
module_fill_tagstruct(c, reply, module);
@@ -3586,24 +3593,30 @@ static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t
}
}
- CHECK_VALIDITY(c->pstream, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, tag, PA_ERR_INVALID);
+ if (!(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE)) {
+ pa_proplist_free(p);
+ CHECK_VALIDITY(c->pstream, FALSE, tag, PA_ERR_INVALID);
+ }
if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST) {
playback_stream *s;
s = pa_idxset_get_by_index(c->output_streams, idx);
- CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
-
+ if (!s || !playback_stream_isinstance(s)) {
+ pa_proplist_free(p);
+ CHECK_VALIDITY(c->pstream, FALSE, tag, PA_ERR_NOENTITY);
+ }
pa_sink_input_update_proplist(s->sink_input, mode, p);
} else if (command == PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST) {
record_stream *s;
- s = pa_idxset_get_by_index(c->record_streams, idx);
- CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
-
+ if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) {
+ pa_proplist_free(p);
+ CHECK_VALIDITY(c->pstream, FALSE, tag, PA_ERR_NOENTITY);
+ }
pa_source_output_update_proplist(s->source_output, mode, p);
+
} else {
pa_assert(command == PA_COMMAND_UPDATE_CLIENT_PROPLIST);
@@ -3611,6 +3624,7 @@ static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t
}
pa_pstream_send_simple_ack(c->pstream, tag);
+ pa_proplist_free(p);
}
static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -4066,7 +4080,7 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag,
CHECK_VALIDITY(c->pstream, m->load_once || idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
cb = (pa_native_protocol_ext_cb_t) (unsigned long) pa_hashmap_get(c->protocol->extensions, m);
- CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
+ CHECK_VALIDITY(c->pstream, cb, tag, PA_ERR_NOEXTENSION);
if (cb(c->protocol, m, c, tag, t) < 0)
protocol_error(c);
@@ -4144,17 +4158,20 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
if (playback_stream_isinstance(stream)) {
playback_stream *ps = PLAYBACK_STREAM(stream);
- if (seek != PA_SEEK_RELATIVE || offset != 0)
- pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset, NULL, NULL);
+ if (chunk->memblock) {
+ if (seek != PA_SEEK_RELATIVE || offset != 0)
+ pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset, NULL, NULL);
- pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+ pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+ } else
+ pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset+chunk->length, NULL, NULL);
} else {
upload_stream *u = UPLOAD_STREAM(stream);
size_t l;
if (!u->memchunk.memblock) {
- if (u->length == chunk->length) {
+ if (u->length == chunk->length && chunk->memblock) {
u->memchunk = *chunk;
pa_memblock_ref(u->memchunk.memblock);
u->length = 0;
@@ -4170,17 +4187,22 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
if (l > chunk->length)
l = chunk->length;
-
if (l > 0) {
- void *src, *dst;
+ void *dst;
dst = pa_memblock_acquire(u->memchunk.memblock);
- src = pa_memblock_acquire(chunk->memblock);
- memcpy((uint8_t*) dst + u->memchunk.index + u->memchunk.length,
- (uint8_t*) src+chunk->index, l);
+ if (chunk->memblock) {
+ void *src;
+ src = pa_memblock_acquire(chunk->memblock);
+
+ memcpy((uint8_t*) dst + u->memchunk.index + u->memchunk.length,
+ (uint8_t*) src + chunk->index, l);
+
+ pa_memblock_release(chunk->memblock);
+ } else
+ pa_silence_memory((uint8_t*) dst + u->memchunk.index + u->memchunk.length, l, &u->sample_spec);
pa_memblock_release(u->memchunk.memblock);
- pa_memblock_release(chunk->memblock);
u->memchunk.length += l;
u->length -= l;
diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c
index 7ff8edc9..ef1105ba 100644
--- a/src/pulsecore/pstream.c
+++ b/src/pulsecore/pstream.c
@@ -832,8 +832,8 @@ static int do_read(pa_pstream *p) {
ntohl(p->read.shm_info[PA_PSTREAM_SHM_INDEX]),
ntohl(p->read.shm_info[PA_PSTREAM_SHM_LENGTH])))) {
- pa_log_warn("Failed to import memory block.");
- return -1;
+ if (pa_log_ratelimit())
+ pa_log_debug("Failed to import memory block.");
}
if (p->recieve_memblock_callback) {
@@ -842,7 +842,7 @@ static int do_read(pa_pstream *p) {
chunk.memblock = b;
chunk.index = 0;
- chunk.length = pa_memblock_get_length(b);
+ chunk.length = b ? pa_memblock_get_length(b) : ntohl(p->read.shm_info[PA_PSTREAM_SHM_LENGTH]);
offset = (int64_t) (
(((uint64_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])) << 32) |
@@ -857,7 +857,8 @@ static int do_read(pa_pstream *p) {
p->recieve_memblock_callback_userdata);
}
- pa_memblock_unref(b);
+ if (b)
+ pa_memblock_unref(b);
}
goto frame_done;
diff --git a/src/pulsecore/rtclock.c b/src/pulsecore/rtclock.c
index 5fc6da2b..dcbd1184 100644
--- a/src/pulsecore/rtclock.c
+++ b/src/pulsecore/rtclock.c
@@ -141,3 +141,11 @@ struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) {
return tv;
}
+
+pa_usec_t pa_timespec_load(const struct timespec *ts) {
+ pa_assert(ts);
+
+ return
+ (pa_usec_t) ts->tv_sec * PA_USEC_PER_SEC +
+ (pa_usec_t) ts->tv_nsec / PA_NSEC_PER_USEC;
+}
diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h
index 281461df..03cc1c72 100644
--- a/src/pulsecore/rtclock.h
+++ b/src/pulsecore/rtclock.h
@@ -42,4 +42,6 @@ void pa_rtclock_hrtimer_enable(void);
struct timeval* pa_rtclock_from_wallclock(struct timeval *tv);
+pa_usec_t pa_timespec_load(const struct timespec *ts);
+
#endif
diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c
index 543262bc..01dfd620 100644
--- a/src/pulsecore/rtpoll.c
+++ b/src/pulsecore/rtpoll.c
@@ -50,29 +50,39 @@
#include <pulsecore/rtsig.h>
#include <pulsecore/flist.h>
#include <pulsecore/core-util.h>
-
#include <pulsecore/winsock.h>
+#include <pulsecore/ratelimit.h>
#include "rtpoll.h"
+/* #define DEBUG_TIMING */
+
struct pa_rtpoll {
struct pollfd *pollfd, *pollfd2;
unsigned n_pollfd_alloc, n_pollfd_used;
- pa_bool_t timer_enabled;
struct timeval next_elapse;
+ pa_bool_t timer_enabled:1;
- pa_bool_t scan_for_dead;
- pa_bool_t running, installed, rebuild_needed, quit;
+ pa_bool_t scan_for_dead:1;
+ pa_bool_t running:1;
+ pa_bool_t installed:1;
+ pa_bool_t rebuild_needed:1;
+ pa_bool_t quit:1;
#ifdef HAVE_PPOLL
+ pa_bool_t timer_armed:1;
+#ifdef __linux__
+ pa_bool_t dont_use_ppoll:1;
+#endif
int rtsig;
sigset_t sigset_unblocked;
timer_t timer;
- pa_bool_t timer_armed;
-#ifdef __linux__
- pa_bool_t dont_use_ppoll;
#endif
+
+#ifdef DEBUG_TIMING
+ pa_usec_t timestamp;
+ pa_usec_t slept, awake;
#endif
PA_LLIST_HEAD(pa_rtpoll_item, items);
@@ -149,6 +159,11 @@ pa_rtpoll *pa_rtpoll_new(void) {
PA_LLIST_HEAD_INIT(pa_rtpoll_item, p->items);
+#ifdef DEBUG_TIMING
+ p->timestamp = pa_rtclock_usec();
+ p->slept = p->awake = 0;
+#endif
+
return p;
}
@@ -156,7 +171,7 @@ void pa_rtpoll_install(pa_rtpoll *p) {
pa_assert(p);
pa_assert(!p->installed);
- p->installed = 1;
+ p->installed = TRUE;
#ifdef HAVE_PPOLL
# ifdef __linux__
@@ -377,6 +392,14 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
pa_timeval_add(&timeout, pa_timeval_diff(&p->next_elapse, &now));
}
+#ifdef DEBUG_TIMING
+ {
+ pa_usec_t now = pa_rtclock_usec();
+ p->awake = now - p->timestamp;
+ p->timestamp = now;
+ }
+#endif
+
/* OK, now let's sleep */
#ifdef HAVE_PPOLL
@@ -396,6 +419,18 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
#endif
r = poll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1);
+#ifdef DEBUG_TIMING
+ {
+ pa_usec_t now = pa_rtclock_usec();
+ p->slept = now - p->timestamp;
+ p->timestamp = now;
+
+ pa_log("Process time %llu ms; sleep time %llu ms",
+ (unsigned long long) (p->awake / PA_USEC_PER_MSEC),
+ (unsigned long long) (p->slept / PA_USEC_PER_MSEC));
+ }
+#endif
+
if (r < 0) {
if (errno == EAGAIN || errno == EINTR)
r = 0;
diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c
index c59d247c..b8c5f786 100644
--- a/src/pulsecore/shm.c
+++ b/src/pulsecore/shm.c
@@ -70,14 +70,14 @@
/* We now put this SHM marker at the end of each segment. It's
* optional, to not require a reboot when upgrading, though */
-struct shm_marker PA_GCC_PACKED {
+struct shm_marker {
pa_atomic_t marker; /* 0xbeefcafe */
pa_atomic_t pid;
uint64_t _reserved1;
uint64_t _reserved2;
uint64_t _reserved3;
uint64_t _reserved4;
-};
+} PA_GCC_PACKED;
static char *segment_name(char *fn, size_t l, unsigned id) {
pa_snprintf(fn, l, "/pulse-shm-%u", id);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 4f39d671..7441e971 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1846,6 +1846,9 @@ void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t m
pa_sink_assert_ref(s);
+ pa_assert(!min_latency || !max_latency ||
+ min_latency <= max_latency);
+
s->thread_info.min_latency = min_latency;
s->thread_info.max_latency = max_latency;
diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c
index 6739effd..dc23bff6 100644
--- a/src/pulsecore/socket-client.c
+++ b/src/pulsecore/socket-client.c
@@ -278,7 +278,11 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size
pa_make_fd_cloexec(c->fd);
+#ifdef HAVE_IPV6
if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)
+#else
+ if (sa->sa_family == AF_INET)
+#endif
pa_make_tcp_socket_low_delay(c->fd);
else
pa_make_socket_low_delay(c->fd);
@@ -353,6 +357,7 @@ void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on
c->userdata = userdata;
}
+#ifdef HAVE_IPV6
pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port) {
struct sockaddr_in6 sa;
@@ -367,6 +372,7 @@ pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[
return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
}
+#endif
#ifdef HAVE_LIBASYNCNS
@@ -470,7 +476,15 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
pa_snprintf(port, sizeof(port), "%u", (unsigned) a.port);
memset(&hints, 0, sizeof(hints));
- hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? PF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? PF_INET6 : PF_UNSPEC);
+ if (a.type == PA_PARSED_ADDRESS_TCP4)
+ hints.ai_family = PF_INET;
+#ifdef HAVE_IPV6
+ else if (a.type == PA_PARSED_ADDRESS_TCP6)
+ hints.ai_family = PF_INET6;
+#endif
+ else
+ hints.ai_family = PF_UNSPEC;
+
hints.ai_socktype = SOCK_STREAM;
#if defined(HAVE_LIBASYNCNS)
@@ -509,11 +523,13 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
struct hostent *host = NULL;
struct sockaddr_in s;
+#ifdef HAVE_IPV6
/* FIXME: PF_INET6 support */
if (hints.ai_family == PF_INET6) {
pa_log_error("IPv6 is not supported on Windows");
goto finish;
}
+#endif
host = gethostbyname(a.path_or_host);
if (!host) {
diff --git a/src/pulsecore/socket-client.h b/src/pulsecore/socket-client.h
index 9ceeaddc..ed36400c 100644
--- a/src/pulsecore/socket-client.h
+++ b/src/pulsecore/socket-client.h
@@ -35,7 +35,9 @@ typedef struct pa_socket_client pa_socket_client;
typedef void (*pa_socket_client_cb_t)(pa_socket_client *c, pa_iochannel*io, void *userdata);
pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port);
+#ifdef HAVE_IPV6
pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port);
+#endif
pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename);
pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen);
pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char *a, uint16_t default_port);
diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c
index a600e0a6..19c2fd09 100644
--- a/src/pulsecore/socket-server.c
+++ b/src/pulsecore/socket-server.c
@@ -289,6 +289,7 @@ fail:
return NULL;
}
+#ifdef HAVE_IPV6
pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port, const char *tcpwrap_service) {
pa_socket_server *ss;
int fd = -1;
@@ -347,6 +348,7 @@ fail:
return NULL;
}
+#endif
pa_socket_server* pa_socket_server_new_ipv4_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
pa_assert(m);
@@ -355,12 +357,14 @@ pa_socket_server* pa_socket_server_new_ipv4_loopback(pa_mainloop_api *m, uint16_
return pa_socket_server_new_ipv4(m, INADDR_LOOPBACK, port, tcpwrap_service);
}
+#ifdef HAVE_IPV6
pa_socket_server* pa_socket_server_new_ipv6_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
pa_assert(m);
pa_assert(port > 0);
return pa_socket_server_new_ipv6(m, in6addr_loopback.s6_addr, port, tcpwrap_service);
}
+#endif
pa_socket_server* pa_socket_server_new_ipv4_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
pa_assert(m);
@@ -369,12 +373,14 @@ pa_socket_server* pa_socket_server_new_ipv4_any(pa_mainloop_api *m, uint16_t por
return pa_socket_server_new_ipv4(m, INADDR_ANY, port, tcpwrap_service);
}
+#ifdef HAVE_IPV6
pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
pa_assert(m);
pa_assert(port > 0);
return pa_socket_server_new_ipv6(m, in6addr_any.s6_addr, port, tcpwrap_service);
}
+#endif
pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service) {
struct in_addr ipv4;
@@ -389,6 +395,7 @@ pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const cha
return NULL;
}
+#ifdef HAVE_IPV6
pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service) {
struct in6_addr ipv6;
@@ -401,6 +408,7 @@ pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const cha
return NULL;
}
+#endif
static void socket_server_free(pa_socket_server*s) {
pa_assert(s);
@@ -441,6 +449,7 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
pa_assert(l > 0);
switch (s->type) {
+#ifdef HAVE_IPV6
case SOCKET_SERVER_IPV6: {
struct sockaddr_in6 sa;
socklen_t sa_len = sizeof(sa);
@@ -476,6 +485,7 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
return c;
}
+#endif
case SOCKET_SERVER_IPV4: {
struct sockaddr_in sa;
diff --git a/src/pulsecore/socket-server.h b/src/pulsecore/socket-server.h
index 1edfb432..b9a2c40e 100644
--- a/src/pulsecore/socket-server.h
+++ b/src/pulsecore/socket-server.h
@@ -34,13 +34,15 @@ typedef struct pa_socket_server pa_socket_server;
pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd);
pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename);
pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, const char *tcpwrap_service);
-pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port, const char *tcpwrap_service);
pa_socket_server* pa_socket_server_new_ipv4_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service);
-pa_socket_server* pa_socket_server_new_ipv6_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service);
pa_socket_server* pa_socket_server_new_ipv4_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service);
-pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service);
pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service);
+#ifdef HAVE_IPV6
+pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv6_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service);
pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service);
+#endif
void pa_socket_server_unref(pa_socket_server*s);
pa_socket_server* pa_socket_server_ref(pa_socket_server *s);
diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c
index a0920025..e44f6460 100644
--- a/src/pulsecore/socket-util.c
+++ b/src/pulsecore/socket-util.c
@@ -93,7 +93,9 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) {
union {
struct sockaddr sa;
struct sockaddr_in in;
+#ifdef HAVE_IPV6
struct sockaddr_in6 in6;
+#endif
#ifdef HAVE_SYS_UN_H
struct sockaddr_un un;
#endif
@@ -112,6 +114,7 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) {
ip & 0xFF,
ntohs(sa.in.sin_port));
return;
+#ifdef HAVE_IPV6
} else if (sa.sa.sa_family == AF_INET6) {
char buf[INET6_ADDRSTRLEN];
const char *res;
@@ -121,6 +124,7 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) {
pa_snprintf(c, l, "TCP/IP client from [%s]:%u", buf, ntohs(sa.in6.sin6_port));
return;
}
+#endif
#ifdef HAVE_SYS_UN_H
} else if (sa.sa.sa_family == AF_UNIX) {
pa_snprintf(c, l, "UNIX socket client");
@@ -298,8 +302,10 @@ pa_bool_t pa_socket_address_is_local(const struct sockaddr *sa) {
case AF_INET:
return ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK;
+#ifdef HAVE_IPV6
case AF_INET6:
return memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0;
+#endif
default:
return FALSE;
@@ -311,7 +317,9 @@ pa_bool_t pa_socket_is_local(int fd) {
union {
struct sockaddr sa;
struct sockaddr_in in;
+#ifdef HAVE_IPV6
struct sockaddr_in6 in6;
+#endif
#ifdef HAVE_SYS_UN_H
struct sockaddr_un un;
#endif
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 0009d85c..c0d6d9ea 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -1120,6 +1120,9 @@ void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec
pa_source_assert_ref(s);
+ pa_assert(!min_latency || !max_latency ||
+ min_latency <= max_latency);
+
s->thread_info.min_latency = min_latency;
s->thread_info.max_latency = max_latency;
diff --git a/src/tests/alsa-time-test.c b/src/tests/alsa-time-test.c
new file mode 100644
index 00000000..3858bf7b
--- /dev/null
+++ b/src/tests/alsa-time-test.c
@@ -0,0 +1,200 @@
+#include <assert.h>
+#include <inttypes.h>
+#include <time.h>
+
+#include <alsa/asoundlib.h>
+
+static uint64_t timespec_us(const struct timespec *ts) {
+ return
+ ts->tv_sec * 1000000LLU +
+ ts->tv_nsec / 1000LLU;
+}
+
+int main(int argc, char *argv[]) {
+ const char *dev;
+ int r;
+ snd_pcm_hw_params_t *hwparams;
+ snd_pcm_sw_params_t *swparams;
+ snd_pcm_status_t *status;
+ snd_pcm_t *pcm;
+ unsigned rate = 44100;
+ unsigned periods = 0;
+ snd_pcm_uframes_t boundary, buffer_size = 44100/10; /* 100s */
+ int dir = 1;
+ struct timespec start, last_timestamp = { 0, 0 };
+ uint64_t start_us;
+ snd_pcm_sframes_t last_avail, last_delay;
+ struct pollfd *pollfds;
+ int n_pollfd;
+ int64_t sample_count = 0;
+
+ snd_pcm_hw_params_alloca(&hwparams);
+ snd_pcm_sw_params_alloca(&swparams);
+ snd_pcm_status_alloca(&status);
+
+ r = clock_gettime(CLOCK_MONOTONIC, &start);
+ assert(r == 0);
+
+ start_us = timespec_us(&start);
+
+ dev = argc > 1 ? argv[1] : "front:AudioPCI";
+
+ r = snd_pcm_open(&pcm, dev, SND_PCM_STREAM_PLAYBACK, 0);
+ assert(r == 0);
+
+ r = snd_pcm_hw_params_any(pcm, hwparams);
+ assert(r == 0);
+
+ r = snd_pcm_hw_params_set_rate_resample(pcm, hwparams, 0);
+ assert(r == 0);
+
+ r = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
+ assert(r == 0);
+
+ r = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16_LE);
+ assert(r == 0);
+
+ r = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, NULL);
+ assert(r == 0);
+
+ r = snd_pcm_hw_params_set_channels(pcm, hwparams, 2);
+ assert(r == 0);
+
+ r = snd_pcm_hw_params_set_periods_integer(pcm, hwparams);
+ assert(r == 0);
+
+ r = snd_pcm_hw_params_set_periods_near(pcm, hwparams, &periods, &dir);
+ assert(r == 0);
+
+ r = snd_pcm_hw_params_set_buffer_size_near(pcm, hwparams, &buffer_size);
+ assert(r == 0);
+
+ r = snd_pcm_hw_params(pcm, hwparams);
+ assert(r == 0);
+
+ r = snd_pcm_hw_params_current(pcm, hwparams);
+ assert(r == 0);
+
+ r = snd_pcm_sw_params_set_avail_min(pcm, swparams, 1);
+ assert(r == 0);
+
+ r = snd_pcm_sw_params_set_period_event(pcm, swparams, 1);
+ assert(r == 0);
+
+ r = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
+ assert(r == 0);
+ r = snd_pcm_sw_params_set_start_threshold(pcm, swparams, buffer_size);
+ assert(r == 0);
+
+ r = snd_pcm_sw_params_get_boundary(swparams, &boundary);
+ assert(r == 0);
+ r = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary);
+ assert(r == 0);
+
+ r = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE);
+ assert(r == 0);
+
+ r = snd_pcm_sw_params(pcm, swparams);
+ assert(r == 0);
+
+ r = snd_pcm_prepare(pcm);
+ assert(r == 0);
+
+ r = snd_pcm_sw_params_current(pcm, swparams);
+ assert(r == 0);
+
+/* assert(snd_pcm_hw_params_is_monotonic(hwparams) > 0); */
+
+ n_pollfd = snd_pcm_poll_descriptors_count(pcm);
+ assert(n_pollfd > 0);
+
+ pollfds = malloc(sizeof(struct pollfd) * n_pollfd);
+ assert(pollfds);
+
+ r = snd_pcm_poll_descriptors(pcm, pollfds, n_pollfd);
+ assert(r == n_pollfd);
+
+ for (;;) {
+ snd_pcm_sframes_t avail, delay;
+/* snd_pcm_uframes_t avail2; */
+ struct timespec now, timestamp;
+ unsigned short revents;
+ int written = 0;
+ uint64_t now_us, timestamp_us;
+ snd_pcm_state_t state;
+
+ r = poll(pollfds, n_pollfd, 0);
+ assert(r >= 0);
+
+ r = snd_pcm_poll_descriptors_revents(pcm, pollfds, n_pollfd, &revents);
+ assert(r == 0);
+
+ assert((revents & ~POLLOUT) == 0);
+
+/* state = snd_pcm_get_state(pcm); */
+
+ avail = snd_pcm_avail(pcm);
+ assert(avail >= 0);
+
+ r = snd_pcm_status(pcm, status);
+ assert(r == 0);
+
+ printf("%lu %lu\n", (unsigned long) avail, (unsigned long) snd_pcm_status_get_avail(status));
+
+ assert(avail == (snd_pcm_sframes_t) snd_pcm_status_get_avail(status));
+ snd_pcm_status_get_htstamp(status, &timestamp);
+ delay = snd_pcm_status_get_delay(status);
+ state = snd_pcm_status_get_state(status);
+
+/* r = snd_pcm_avail_delay(pcm, &avail, &delay); */
+/* assert(r == 0); */
+
+/* r = snd_pcm_htimestamp(pcm, &avail2, &timestamp); */
+/* assert(r == 0); */
+
+/* assert(avail == (snd_pcm_sframes_t) avail2); */
+
+ r = clock_gettime(CLOCK_MONOTONIC, &now);
+ assert(r == 0);
+
+ assert(!revents || avail > 0);
+
+ if (avail) {
+ snd_pcm_sframes_t sframes;
+ static const uint16_t samples[2] = { 0, 0 };
+
+ sframes = snd_pcm_writei(pcm, samples, 1);
+ assert(sframes == 1);
+
+ written = 1;
+ sample_count++;
+ }
+
+ if (!written &&
+ memcmp(&timestamp, &last_timestamp, sizeof(timestamp)) == 0 &&
+ avail == last_avail &&
+ delay == last_delay) {
+ /* This is boring */
+ continue;
+ }
+
+ now_us = timespec_us(&now);
+ timestamp_us = timespec_us(&timestamp);
+
+ printf("%llu\t%llu\t%llu\t%li\t%li\t%i\t%i\t%i\n",
+ (unsigned long long) (now_us - start_us),
+ (unsigned long long) (timestamp_us ? timestamp_us - start_us : 0),
+ (unsigned long long) ((sample_count - 1 - delay) * 1000000LU / 44100),
+ (signed long) avail,
+ (signed long) delay,
+ revents,
+ written,
+ state);
+
+ last_avail = avail;
+ last_delay = delay;
+ last_timestamp = timestamp;
+ }
+
+ return 0;
+}
diff --git a/src/tests/gtk-test.c b/src/tests/gtk-test.c
new file mode 100644
index 00000000..a2d3e69a
--- /dev/null
+++ b/src/tests/gtk-test.c
@@ -0,0 +1,62 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+#include <pulse/context.h>
+#include <pulse/glib-mainloop.h>
+
+int main(int argc, char *argv[]) {
+
+ pa_context *c;
+ pa_glib_mainloop *m;
+ GtkWidget *window;
+ int r;
+
+ gtk_init(&argc, &argv);
+
+ g_set_application_name("This is a test");
+ gtk_window_set_default_icon_name("foobar");
+ g_setenv("PULSE_PROP_media.role", "phone", TRUE);
+
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW (window), g_get_application_name());
+ gtk_widget_show_all(window);
+
+ 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);
+
+ gtk_main();
+
+ pa_context_unref(c);
+ pa_glib_mainloop_free(m);
+
+ return 0;
+}
diff --git a/src/tests/interpol-test.c b/src/tests/interpol-test.c
index 9d930774..d7da660c 100644
--- a/src/tests/interpol-test.c
+++ b/src/tests/interpol-test.c
@@ -39,10 +39,23 @@
static pa_context *context = NULL;
static pa_stream *stream = NULL;
static pa_mainloop_api *mainloop_api = NULL;
+static pa_bool_t playback = TRUE;
static void stream_write_cb(pa_stream *p, size_t nbytes, void *userdata) {
/* Just some silence */
- pa_stream_write(p, pa_xmalloc0(nbytes), nbytes, pa_xfree, 0, PA_SEEK_RELATIVE);
+ pa_assert_se(pa_stream_write(p, pa_xmalloc0(nbytes), nbytes, pa_xfree, 0, PA_SEEK_RELATIVE) == 0);
+}
+
+static void stream_read_cb(pa_stream *p, size_t nbytes, void *userdata) {
+ /* We don't care, just drop the data */
+
+ while (pa_stream_readable_size(p) > 0) {
+ const void *d;
+ size_t b;
+
+ pa_assert_se(pa_stream_peek(p, &d, &b) == 0);
+ pa_assert_se(pa_stream_drop(p) == 0);
+ }
}
/* This is called whenever the context status changes */
@@ -68,8 +81,13 @@ static void context_state_callback(pa_context *c, void *userdata) {
stream = pa_stream_new(c, "interpol-test", &ss, NULL);
assert(stream);
- pa_stream_connect_playback(stream, NULL, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
- pa_stream_set_write_callback(stream, stream_write_cb, NULL);
+ if (playback) {
+ pa_assert_se(pa_stream_connect_playback(stream, NULL, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) == 0);
+ pa_stream_set_write_callback(stream, stream_write_cb, NULL);
+ } else {
+ pa_assert_se(pa_stream_connect_record(stream, NULL, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE) == 0);
+ pa_stream_set_read_callback(stream, stream_read_cb, NULL);
+ }
break;
}
@@ -90,6 +108,8 @@ int main(int argc, char *argv[]) {
struct timeval start, last_info = { 0, 0 };
pa_usec_t old_t = 0, old_rtc = 0;
+ playback = argc <= 1 || !pa_streq(argv[1], "-r");
+
/* Set up a new main loop */
m = pa_threaded_mainloop_new();
assert(m);
@@ -106,7 +126,8 @@ int main(int argc, char *argv[]) {
pa_gettimeofday(&start);
- pa_threaded_mainloop_start(m);
+ r = pa_threaded_mainloop_start(m);
+ assert(r >= 0);
for (k = 0; k < 5000; k++) {
pa_bool_t success = FALSE, changed = FALSE;
@@ -138,7 +159,14 @@ int main(int argc, char *argv[]) {
if (success) {
rtc = pa_timeval_diff(&now, &start);
- printf("%i\t%llu\t%llu\t%llu\t%llu\t%u\t%u\n", k, (unsigned long long) rtc, (unsigned long long) t, (unsigned long long) (rtc-old_rtc), (unsigned long long) (t-old_t), changed, playing);
+ printf("%i\t%llu\t%llu\t%llu\t%llu\t%u\t%u\n", k,
+ (unsigned long long) rtc,
+ (unsigned long long) t,
+ (unsigned long long) (rtc-old_rtc),
+ (unsigned long long) (t-old_t),
+ changed,
+ playing);
+
fflush(stdout);
old_t = t;
old_rtc = rtc;
diff --git a/src/tests/ipacl-test.c b/src/tests/ipacl-test.c
index bcdd469a..f89665cd 100644
--- a/src/tests/ipacl-test.c
+++ b/src/tests/ipacl-test.c
@@ -25,12 +25,15 @@
#endif
#include "../pulsecore/winsock.h"
+#include "../pulsecore/macro.h"
#include <pulsecore/ipacl.h>
int main(int argc, char *argv[]) {
struct sockaddr_in sa;
+#ifdef HAVE_IPV6
struct sockaddr_in6 sa6;
+#endif
int fd;
int r;
pa_ip_acl *acl;
@@ -87,13 +90,14 @@ int main(int argc, char *argv[]) {
close(fd);
+#ifdef HAVE_IPV6
fd = socket(PF_INET6, SOCK_STREAM, 0);
assert(fd >= 0);
memset(&sa6, 0, sizeof(sa6));
sa6.sin6_family = AF_INET6;
sa6.sin6_port = htons(22);
- inet_pton(AF_INET6, "::1", &sa6.sin6_addr);
+ pa_assert_se(inet_pton(AF_INET6, "::1", &sa6.sin6_addr) == 1);
r = connect(fd, (struct sockaddr*) &sa6, sizeof(sa6));
assert(r >= 0);
@@ -129,6 +133,7 @@ int main(int argc, char *argv[]) {
pa_ip_acl_free(acl);
close(fd);
+#endif
return 0;
}
diff --git a/src/tests/sync-playback.c b/src/tests/sync-playback.c
index 42c479a1..f2a15601 100644
--- a/src/tests/sync-playback.c
+++ b/src/tests/sync-playback.c
@@ -174,11 +174,16 @@ int main(int argc, char *argv[]) {
pa_context_set_state_callback(context, context_state_callback, NULL);
- pa_context_connect(context, NULL, 0, NULL);
+ /* Connect the context */
+ if (pa_context_connect(context, NULL, 0, NULL) < 0) {
+ fprintf(stderr, "pa_context_connect() failed.\n");
+ goto quit;
+ }
if (pa_mainloop_run(m, &ret) < 0)
fprintf(stderr, "pa_mainloop_run() failed.\n");
+quit:
pa_context_unref(context);
for (i = 0; i < NSTREAMS; i++)
diff --git a/src/tests/thread-mainloop-test.c b/src/tests/thread-mainloop-test.c
index 263cd57d..3bcf4f16 100644
--- a/src/tests/thread-mainloop-test.c
+++ b/src/tests/thread-mainloop-test.c
@@ -47,7 +47,7 @@ int main(int argc, char *argv[]) {
pa_assert_se(m = pa_threaded_mainloop_new());
pa_assert_se(a = pa_threaded_mainloop_get_api(m));
- pa_threaded_mainloop_start(m);
+ pa_assert_se(pa_threaded_mainloop_start(m) >= 0);
pa_threaded_mainloop_lock(m);
diff --git a/src/utils/pacat.c b/src/utils/pacat.c
index 10015ce4..2224da9a 100644
--- a/src/utils/pacat.c
+++ b/src/utils/pacat.c
@@ -336,7 +336,6 @@ static void context_drain_complete(pa_context*c, void *userdata) {
/* Stream draining complete */
static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
- pa_operation *o;
if (!success) {
fprintf(stderr, _("Failed to drain stream: %s\n"), pa_strerror(pa_context_errno(context)));
@@ -350,7 +349,7 @@ static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
pa_stream_unref(stream);
stream = NULL;
- if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
+ if (!pa_context_drain(context, context_drain_complete, NULL))
pa_context_disconnect(context);
else {
if (verbose)
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 154e7f9c..d3da90e6 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -649,6 +649,7 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
pa_xfree(d);
fprintf(stderr, _("Premature end of file\n"));
quit(1);
+ return;
}
pa_stream_write(s, d, length, pa_xfree, 0, PA_SEEK_RELATIVE);
@@ -1029,7 +1030,10 @@ int main(int argc, char *argv[]) {
}
pa_context_set_state_callback(context, context_state_callback, NULL);
- pa_context_connect(context, server, 0, NULL);
+ if (pa_context_connect(context, server, 0, NULL) < 0) {
+ fprintf(stderr, _("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
+ goto quit;
+ }
if (pa_mainloop_run(m, &ret) < 0) {
fprintf(stderr, _("pa_mainloop_run() failed.\n"));
diff --git a/src/utils/padsp.c b/src/utils/padsp.c
index 046bae45..76e86c8d 100644
--- a/src/utils/padsp.c
+++ b/src/utils/padsp.c
@@ -1202,7 +1202,7 @@ fail:
static void sink_info_cb(pa_context *context, const pa_sink_info *si, int eol, void *userdata) {
fd_info *i = userdata;
- if (!si && eol < 0) {
+ if (!si || eol < 0) {
i->operation_success = 0;
pa_threaded_mainloop_signal(i->mainloop, 0);
return;
@@ -1224,7 +1224,7 @@ static void sink_info_cb(pa_context *context, const pa_sink_info *si, int eol, v
static void source_info_cb(pa_context *context, const pa_source_info *si, int eol, void *userdata) {
fd_info *i = userdata;
- if (!si && eol < 0) {
+ if (!si || eol < 0) {
i->operation_success = 0;
pa_threaded_mainloop_signal(i->mainloop, 0);
return;
diff --git a/src/utils/paplay.c b/src/utils/paplay.c
index df2edf62..dec80e5c 100644
--- a/src/utils/paplay.c
+++ b/src/utils/paplay.c
@@ -400,7 +400,10 @@ int main(int argc, char *argv[]) {
pa_context_set_state_callback(context, context_state_callback, NULL);
/* Connect the context */
- pa_context_connect(context, server, 0, NULL);
+ if (pa_context_connect(context, server, 0, NULL) < 0) {
+ fprintf(stderr, _("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
+ goto quit;
+ }
/* Run the main loop */
if (pa_mainloop_run(m, &ret) < 0) {