diff options
Diffstat (limited to 'src')
214 files changed, 12424 insertions, 3251 deletions
diff --git a/src/.gitignore b/src/.gitignore index 6902eb9f..72c38cc6 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,3 +1,5 @@ +prioq-test +lock-autospawn-test  *.lo  *.o  *.la @@ -7,7 +9,7 @@ Makefile  Makefile.in  asyncmsgq-test  asyncq-test -bt-proximity-helper +proximity-helper  channelmap-test  client.conf  close-test diff --git a/src/Makefile.am b/src/Makefile.am index 29a6ef47..b9d00831 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -45,28 +45,31 @@ endif  #     Compiler/linker flags       #  ################################### -AM_CFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src/modules -I$(top_builddir)/src/modules/rtp -I$(top_builddir)/src/modules/gconf -AM_CFLAGS += $(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS -AM_CFLAGS += $(LTDLINCL) -AM_CFLAGS += $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) $(LIBSPEEX_CFLAGS) -AM_CFLAGS += -DPA_DLSEARCHPATH=\"$(modlibexecdir)\" -AM_CFLAGS += -DPA_DEFAULT_CONFIG_DIR=\"$(PA_DEFAULT_CONFIG_DIR)\" -AM_CFLAGS += -DPA_BINARY=\"$(PA_BINARY)\" -AM_CFLAGS += -DPA_SYSTEM_RUNTIME_PATH=\"$(PA_SYSTEM_RUNTIME_PATH)\" -AM_CFLAGS += -DPA_SYSTEM_CONFIG_PATH=\"$(PA_SYSTEM_CONFIG_PATH)\" -AM_CFLAGS += -DPA_SYSTEM_STATE_PATH=\"$(PA_SYSTEM_STATE_PATH)\" -AM_CFLAGS += -DAO_REQUIRE_CAS -AM_CFLAGS += -DPULSE_LOCALEDIR=\"$(pulselocaledir)\" -AM_CFLAGS += -DPA_MACHINE_ID=\"$(localstatedir)/lib/dbus/machine-id\" - -# This cool debug trap works on i386/gcc only -AM_CFLAGS += '-DDEBUG_TRAP=__asm__("int $$3")' - -AM_LIBADD = $(PTHREAD_LIBS) -AM_LDADD = $(PTHREAD_LIBS) - -# Only required on some platforms but defined for all to avoid errors -AM_LDFLAGS = -Wl,-no-undefined -Wl,--gc-sections +AM_CFLAGS = \ +	-I$(top_srcdir)/src \ +	-I$(top_builddir)/src/modules \ +	-I$(top_builddir)/src/modules/rtp \ +	-I$(top_builddir)/src/modules/gconf \ +	-I$(top_builddir)/src/modules/bluetooth \ +	-I$(top_builddir)/src/modules/raop \ +	$(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS \ +	$(LTDLINCL) \ +	$(LIBSAMPLERATE_CFLAGS) \ +	$(LIBSNDFILE_CFLAGS) \ +	$(LIBSPEEX_CFLAGS) \ +	-DPA_DLSEARCHPATH=\"$(modlibexecdir)\" \ +	-DPA_DEFAULT_CONFIG_DIR=\"$(PA_DEFAULT_CONFIG_DIR)\" \ +	-DPA_BINARY=\"$(PA_BINARY)\" \ +	-DPA_SYSTEM_RUNTIME_PATH=\"$(PA_SYSTEM_RUNTIME_PATH)\" \ +	-DPA_SYSTEM_CONFIG_PATH=\"$(PA_SYSTEM_CONFIG_PATH)\" \ +	-DPA_SYSTEM_STATE_PATH=\"$(PA_SYSTEM_STATE_PATH)\" \ +	-DAO_REQUIRE_CAS \ +	-DPULSE_LOCALEDIR=\"$(pulselocaledir)\" \ +	-DPA_MACHINE_ID=\"$(localstatedir)/lib/dbus/machine-id\" + +AM_LIBADD = $(PTHREAD_LIBS) $(INTLLIBS) +AM_LDADD = $(PTHREAD_LIBS) $(INTLLIBS) +AM_LDFLAGS = -Wl,-z,nodelete  if STATIC_BINS  BINLDFLAGS = -static @@ -77,18 +80,6 @@ AM_LDFLAGS+=-Wl,--export-all-symbols  WINSOCK_LIBS=-lwsock32 -lws2_32 -lwininet  endif -if OS_IS_WIN32 -PA_THREAD_OBJS = \ -		pulsecore/mutex-win32.c pulsecore/mutex.h \ -		pulsecore/thread-win32.c pulsecore/thread.h \ -		pulsecore/semaphore-win32.c pulsecore/semaphore.h -else -PA_THREAD_OBJS = \ -		pulsecore/mutex-posix.c pulsecore/mutex.h \ -		pulsecore/thread-posix.c pulsecore/thread.h \ -		pulsecore/semaphore-posix.c pulsecore/semaphore.h -endif -  ###################################  #          Extra files            #  ################################### @@ -139,10 +130,9 @@ pulseaudio_SOURCES = \  		daemon/main.c  pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS) -pulseaudio_CPPFLAGS = $(AM_CPPFLAGS) -pulseaudio_LDADD = $(AM_LDADD) libpulsecore.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) $(DBUS_LIBS) +pulseaudio_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) $(DBUS_LIBS)  # This is needed because automake doesn't properly expand the foreach below -pulseaudio_DEPENDENCIES = libpulsecore.la $(PREOPEN_LIBS) +pulseaudio_DEPENDENCIES = libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la $(PREOPEN_LIBS)  if PREOPEN_MODS  PREOPEN_LIBS = $(PREOPEN_MODS) @@ -157,14 +147,11 @@ pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -dlopen force $(foreach f,$(PRE  endif  if HAVE_POLKIT -  policy_DATA = daemon/org.pulseaudio.policy  pulseaudio_SOURCES += daemon/polkit.c daemon/polkit.h  pulseaudio_CFLAGS += $(POLKIT_CFLAGS)  pulseaudio_LDADD += $(POLKIT_LIBS) - -  endif  ################################### @@ -201,24 +188,24 @@ paplay_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)  paplay_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)  paplay_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -pactl_SOURCES = utils/pactl.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS) -pactl_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS) +pactl_SOURCES = utils/pactl.c +pactl_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la $(LIBSNDFILE_LIBS)  pactl_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)  pactl_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -pasuspender_SOURCES = utils/pasuspender.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS) -pasuspender_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS) +pasuspender_SOURCES = utils/pasuspender.c +pasuspender_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la $(LIBSNDFILE_LIBS)  pasuspender_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)  pasuspender_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -pacmd_SOURCES = utils/pacmd.c pulsecore/pid.c pulsecore/pid.h pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS) +pacmd_SOURCES = utils/pacmd.c  pacmd_CFLAGS = $(AM_CFLAGS) -pacmd_LDADD = $(AM_LDADD) libpulse.la +pacmd_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la  pacmd_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -pax11publish_SOURCES = utils/pax11publish.c pulsecore/x11prop.c pulsecore/x11prop.h pulse/client-conf.c pulse/client-conf.h pulsecore/authkey.h pulsecore/authkey.c pulsecore/random.h pulsecore/random.c pulsecore/conf-parser.c pulsecore/conf-parser.h pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h  pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS) +pax11publish_SOURCES = utils/pax11publish.c  pax11publish_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS) -pax11publish_LDADD = $(AM_LDADD) libpulse.la $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) +pax11publish_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)  pax11publish_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  pabrowse_SOURCES = utils/pabrowse.c @@ -262,7 +249,9 @@ noinst_PROGRAMS = \  		envelope-test \  		proplist-test \  		rtstutter \ -		stripnul +		stripnul \ +		lock-autospawn-test \ +		prioq-test  if HAVE_SIGXCPU  noinst_PROGRAMS += \ @@ -282,12 +271,12 @@ mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  thread_mainloop_test_SOURCES = tests/thread-mainloop-test.c  thread_mainloop_test_CFLAGS = $(AM_CFLAGS) -thread_mainloop_test_LDADD = $(AM_LDADD) libpulsecore.la libpulse.la +thread_mainloop_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la  thread_mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  utf8_test_SOURCES = tests/utf8-test.c  utf8_test_CFLAGS = $(AM_CFLAGS) -utf8_test_LDADD = $(AM_LDADD) libpulsecore.la +utf8_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  utf8_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  get_binary_name_test_SOURCES = tests/get-binary-name-test.c @@ -295,56 +284,54 @@ get_binary_name_test_CFLAGS = $(AM_CFLAGS)  get_binary_name_test_LDADD = $(AM_LDADD) libpulse.la  get_binary_name_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -ipacl_test_SOURCES = tests/ipacl-test.c \ -		pulsecore/ipacl.c pulsecore/ipacl.h \ -		pulsecore/inet_pton.c pulsecore/inet_pton.h +ipacl_test_SOURCES = tests/ipacl-test.c  ipacl_test_CFLAGS = $(AM_CFLAGS) -ipacl_test_LDADD = $(AM_LDADD) libpulsecore.la +ipacl_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  ipacl_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  hook_list_test_SOURCES = tests/hook-list-test.c  hook_list_test_CFLAGS = $(AM_CFLAGS) -hook_list_test_LDADD = $(AM_LDADD) libpulsecore.la +hook_list_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  hook_list_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  memblock_test_SOURCES = tests/memblock-test.c  memblock_test_CFLAGS = $(AM_CFLAGS) -memblock_test_LDADD = $(AM_LDADD) libpulsecore.la +memblock_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  memblock_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  thread_test_SOURCES = tests/thread-test.c  thread_test_CFLAGS = $(AM_CFLAGS) -thread_test_LDADD = $(AM_LDADD) libpulsecore.la +thread_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  thread_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  flist_test_SOURCES = tests/flist-test.c  flist_test_CFLAGS = $(AM_CFLAGS) -flist_test_LDADD = $(AM_LDADD) libpulsecore.la +flist_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  flist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  asyncq_test_SOURCES = tests/asyncq-test.c  asyncq_test_CFLAGS = $(AM_CFLAGS) -asyncq_test_LDADD = $(AM_LDADD) libpulsecore.la +asyncq_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  asyncq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  asyncmsgq_test_SOURCES = tests/asyncmsgq-test.c  asyncmsgq_test_CFLAGS = $(AM_CFLAGS) -asyncmsgq_test_LDADD = $(AM_LDADD) libpulsecore.la +asyncmsgq_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  asyncmsgq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  queue_test_SOURCES = tests/queue-test.c  queue_test_CFLAGS = $(AM_CFLAGS) -queue_test_LDADD = $(AM_LDADD) libpulsecore.la +queue_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  queue_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  rtpoll_test_SOURCES = tests/rtpoll-test.c  rtpoll_test_CFLAGS = $(AM_CFLAGS) -rtpoll_test_LDADD = $(AM_LDADD) libpulsecore.la +rtpoll_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  rtpoll_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  mcalign_test_SOURCES = tests/mcalign-test.c  mcalign_test_CFLAGS = $(AM_CFLAGS) -mcalign_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la +mcalign_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  mcalign_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  pacat_simple_SOURCES = tests/pacat-simple.c @@ -359,12 +346,12 @@ parec_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  strlist_test_SOURCES = tests/strlist-test.c  strlist_test_CFLAGS = $(AM_CFLAGS) -strlist_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la libstrlist.la +strlist_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  strlist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  close_test_SOURCES = tests/close-test.c  close_test_CFLAGS = $(AM_CFLAGS) -close_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la libstrlist.la +close_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  close_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  voltest_SOURCES = tests/voltest.c @@ -379,12 +366,12 @@ channelmap_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  cpulimit_test_SOURCES = tests/cpulimit-test.c daemon/cpulimit.c daemon/cpulimit.h  cpulimit_test_CFLAGS = $(AM_CFLAGS) -cpulimit_test_LDADD = $(AM_LDADD) libpulsecore.la +cpulimit_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  cpulimit_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  cpulimit_test2_SOURCES = tests/cpulimit-test.c daemon/cpulimit.c daemon/cpulimit.h  cpulimit_test2_CFLAGS = $(AM_CFLAGS) -DTEST2 -cpulimit_test2_LDADD = $(AM_LDADD) libpulsecore.la +cpulimit_test2_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  cpulimit_test2_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  mainloop_test_glib_SOURCES = $(mainloop_test_SOURCES) @@ -394,7 +381,7 @@ mainloop_test_glib_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  memblockq_test_SOURCES = tests/memblockq-test.c  memblockq_test_CFLAGS = $(AM_CFLAGS) -memblockq_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la +memblockq_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  memblockq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  sync_playback_SOURCES = tests/sync-playback.c @@ -403,55 +390,160 @@ sync_playback_CFLAGS = $(AM_CFLAGS)  sync_playback_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  interpol_test_SOURCES = tests/interpol-test.c -interpol_test_LDADD = $(AM_LDADD) libpulse.la libpulsecore.la +interpol_test_LDADD = $(AM_LDADD) libpulse.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  interpol_test_CFLAGS = $(AM_CFLAGS)  interpol_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  sig2str_test_SOURCES = tests/sig2str-test.c -sig2str_test_LDADD = $(AM_LDADD) libpulsecore.la +sig2str_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  sig2str_test_CFLAGS = $(AM_CFLAGS)  sig2str_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  resampler_test_SOURCES = tests/resampler-test.c -resampler_test_LDADD = $(AM_LDADD) libpulsecore.la +resampler_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  resampler_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)  resampler_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)  mix_test_SOURCES = tests/mix-test.c -mix_test_LDADD = $(AM_LDADD) libpulsecore.la +mix_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  mix_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)  mix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)  remix_test_SOURCES = tests/remix-test.c -remix_test_LDADD = $(AM_LDADD) libpulsecore.la +remix_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  remix_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)  remix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)  smoother_test_SOURCES = tests/smoother-test.c -smoother_test_LDADD = $(AM_LDADD) libpulsecore.la +smoother_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  smoother_test_CFLAGS = $(AM_CFLAGS)  smoother_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  envelope_test_SOURCES = tests/envelope-test.c -envelope_test_LDADD = $(AM_LDADD) libpulsecore.la +envelope_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  envelope_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)  envelope_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)  proplist_test_SOURCES = tests/proplist-test.c -proplist_test_LDADD = $(AM_LDADD) libpulsecore.la +proplist_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  proplist_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)  proplist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)  rtstutter_SOURCES = tests/rtstutter.c -rtstutter_LDADD = $(AM_LDADD) libpulsecore.la +rtstutter_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  rtstutter_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)  rtstutter_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)  stripnul_SOURCES = tests/stripnul.c -stripnul_LDADD = $(AM_LDADD) libpulsecore.la +stripnul_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la  stripnul_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)  stripnul_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS) +lock_autospawn_test_SOURCES = tests/lock-autospawn-test.c +lock_autospawn_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la +lock_autospawn_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) +lock_autospawn_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS) + +prioq_test_SOURCES = tests/prioq-test.c +prioq_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la +prioq_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) +prioq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS) + +################################### +#         Common library          # +################################### + +lib_LTLIBRARIES = \ +		libpulsecommon-@PA_MAJORMINORMICRO@.la + +libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES = \ +		pulse/client-conf.c pulse/client-conf.h \ +		pulse/i18n.c pulse/i18n.h \ +		pulsecore/authkey.c pulsecore/authkey.h \ +		pulsecore/conf-parser.c pulsecore/conf-parser.h \ +		pulsecore/core-error.c pulsecore/core-error.h \ +		pulsecore/core-util.c pulsecore/core-util.h \ +		pulsecore/creds.h \ +		pulsecore/dynarray.c pulsecore/dynarray.h \ +		pulsecore/endianmacros.h \ +		pulsecore/flist.c pulsecore/flist.h \ +		pulsecore/hashmap.c pulsecore/hashmap.h \ +		pulsecore/idxset.c pulsecore/idxset.h \ +		pulsecore/inet_ntop.c pulsecore/inet_ntop.h \ +		pulsecore/inet_pton.c pulsecore/inet_pton.h \ +		pulsecore/iochannel.c pulsecore/iochannel.h \ +		pulsecore/ioline.c pulsecore/ioline.h \ +		pulsecore/ipacl.h pulsecore/ipacl.c \ +		pulsecore/llist.h \ +		pulsecore/lock-autospawn.c pulsecore/lock-autospawn.h \ +		pulsecore/log.c pulsecore/log.h \ +		pulsecore/macro.h \ +		pulsecore/mcalign.c pulsecore/mcalign.h \ +		pulsecore/memblock.c pulsecore/memblock.h \ +		pulsecore/memblockq.c pulsecore/memblockq.h \ +		pulsecore/memchunk.c pulsecore/memchunk.h \ +		pulsecore/native-common.h \ +		pulsecore/once.c pulsecore/once.h \ +		pulsecore/packet.c pulsecore/packet.h \ +		pulsecore/parseaddr.c pulsecore/parseaddr.h \ +		pulsecore/pdispatch.c pulsecore/pdispatch.h \ +		pulsecore/pid.c pulsecore/pid.h \ +		pulsecore/pipe.c pulsecore/pipe.h \ +		pulsecore/poll.c pulsecore/poll.h \ +		pulsecore/prioq.c pulsecore/prioq.h \ +		pulsecore/proplist-util.c pulsecore/proplist-util.h \ +		pulsecore/pstream-util.c pulsecore/pstream-util.h \ +		pulsecore/pstream.c pulsecore/pstream.h \ +		pulsecore/queue.c pulsecore/queue.h \ +		pulsecore/random.c pulsecore/random.h \ +		pulsecore/rtclock.c pulsecore/rtclock.h \ +		pulsecore/shm.c pulsecore/shm.h \ +		pulsecore/socket-client.c pulsecore/socket-client.h \ +		pulsecore/socket-server.c pulsecore/socket-server.h \ +		pulsecore/socket-util.c pulsecore/socket-util.h \ +		pulsecore/strbuf.c pulsecore/strbuf.h \ +		pulsecore/strlist.c pulsecore/strlist.h \ +		pulsecore/tagstruct.c pulsecore/tagstruct.h \ +		pulsecore/time-smoother.c pulsecore/time-smoother.h \ +		pulsecore/tokenizer.c pulsecore/tokenizer.h \ +		pulsecore/winsock.h + +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) + +## 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 +## libpulsecommon only includes unofficial APIs. + +if OS_IS_WIN32 +libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES += \ +		pulsecore/mutex-win32.c pulsecore/mutex.h \ +		pulsecore/thread-win32.c pulsecore/thread.h \ +		pulsecore/semaphore-win32.c pulsecore/semaphore.h +else +libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES += \ +		pulsecore/mutex-posix.c pulsecore/mutex.h \ +		pulsecore/thread-posix.c pulsecore/thread.h \ +		pulsecore/semaphore-posix.c pulsecore/semaphore.h +endif + +if HAVE_X11 +libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/x11prop.c pulsecore/x11prop.h +libpulsecommon_@PA_MAJORMINORMICRO@_la_CFLAGS += $(X_CFLAGS) +libpulsecommon_@PA_MAJORMINORMICRO@_la_LDFLAGS += $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) +endif + +if HAVE_LIBASYNCNS +libpulsecommon_@PA_MAJORMINORMICRO@_la_CFLAGS += $(LIBASYNCNS_CFLAGS) +libpulsecommon_@PA_MAJORMINORMICRO@_la_LIBADD += $(LIBASYNCNS_LIBS) +endif + +if OS_IS_WIN32 +libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/dllmain.c +endif +  ###################################  #         Client library          #  ################################### @@ -462,11 +554,14 @@ pulseinclude_HEADERS = \  		pulse/context.h \  		pulse/def.h \  		pulse/error.h \ +		pulse/ext-stream-restore.h \ +		pulse/gccmacro.h \  		pulse/introspect.h \ -		pulse/mainloop.h \  		pulse/mainloop-api.h \  		pulse/mainloop-signal.h \ +		pulse/mainloop.h \  		pulse/operation.h \ +		pulse/proplist.h \  		pulse/pulseaudio.h \  		pulse/sample.h \  		pulse/scache.h \ @@ -479,31 +574,24 @@ pulseinclude_HEADERS = \  		pulse/util.h \  		pulse/version.h \  		pulse/volume.h \ -		pulse/xmalloc.h \ -		pulse/proplist.h \ -		pulse/gccmacro.h \ -		pulse/ext-stream-restore.h - -if HAVE_AVAHI -pulseinclude_HEADERS += \ -		pulse/browser.h -endif - -if HAVE_GLIB20 -pulseinclude_HEADERS += \ -		pulse/glib-mainloop.h -endif +		pulse/xmalloc.h -lib_LTLIBRARIES = \ +lib_LTLIBRARIES += \  		libpulse.la \  		libpulse-simple.la  if HAVE_AVAHI +pulseinclude_HEADERS += \ +		pulse/browser.h +  lib_LTLIBRARIES += \  		libpulse-browse.la  endif  if HAVE_GLIB20 +pulseinclude_HEADERS += \ +		pulse/glib-mainloop.h +  lib_LTLIBRARIES += \  		libpulse-mainloop-glib.la  endif @@ -512,16 +600,18 @@ endif  libpulse_la_SOURCES = \  		pulse/cdecl.h \  		pulse/channelmap.c pulse/channelmap.h \ -		pulse/client-conf.c pulse/client-conf.h \  		pulse/context.c pulse/context.h \  		pulse/def.h \  		pulse/error.c pulse/error.h \ +		pulse/ext-stream-restore.c pulse/ext-stream-restore.h \ +		pulse/gccmacro.h \  		pulse/internal.h \  		pulse/introspect.c pulse/introspect.h \ -		pulse/mainloop.c pulse/mainloop.h \  		pulse/mainloop-api.c pulse/mainloop-api.h \  		pulse/mainloop-signal.c pulse/mainloop-signal.h \ +		pulse/mainloop.c pulse/mainloop.h \  		pulse/operation.c pulse/operation.h \ +		pulse/proplist.c pulse/proplist.h \  		pulse/pulseaudio.h \  		pulse/sample.c pulse/sample.h \  		pulse/scache.c pulse/scache.h \ @@ -532,339 +622,121 @@ libpulse_la_SOURCES = \  		pulse/utf8.c pulse/utf8.h \  		pulse/util.c pulse/util.h \  		pulse/volume.c pulse/volume.h \ -		pulse/xmalloc.c pulse/xmalloc.h \ -		pulse/proplist.c pulse/proplist.h \ -		pulse/ext-stream-restore.c pulse/ext-stream-restore.h \ -		pulse/i18n.c pulse/i18n.h - -# Internal stuff that is shared with libpulsecore -libpulse_la_SOURCES += \ -		pulsecore/authkey.c pulsecore/authkey.h \ -		pulsecore/conf-parser.c pulsecore/conf-parser.h \ -		pulsecore/core-util.c pulsecore/core-util.h \ -		pulsecore/dynarray.c pulsecore/dynarray.h \ -		pulsecore/hashmap.c pulsecore/hashmap.h \ -		pulsecore/idxset.c pulsecore/idxset.h \ -		pulsecore/inet_ntop.c pulsecore/inet_ntop.h \ -		pulsecore/iochannel.c pulsecore/iochannel.h \ -		pulsecore/llist.h \ -		pulsecore/log.c pulsecore/log.h \ -		pulsecore/mcalign.c pulsecore/mcalign.h \ -		pulsecore/memblock.c pulsecore/memblock.h \ -		pulsecore/memblockq.c pulsecore/memblockq.h \ -		pulsecore/memchunk.c pulsecore/memchunk.h \ -		pulsecore/native-common.h \ -		pulsecore/packet.c pulsecore/packet.h \ -		pulsecore/parseaddr.c pulsecore/parseaddr.h \ -		pulsecore/pdispatch.c pulsecore/pdispatch.h \ -		pulsecore/pipe.c pulsecore/pipe.h \ -		pulsecore/poll.c pulsecore/poll.h \ -		pulsecore/pstream.c pulsecore/pstream.h \ -		pulsecore/pstream-util.c pulsecore/pstream-util.h \ -		pulsecore/queue.c pulsecore/queue.h \ -		pulsecore/random.c pulsecore/random.h \ -		pulsecore/socket-client.c pulsecore/socket-client.h \ -		pulsecore/socket-util.c pulsecore/socket-util.h \ -		pulsecore/strbuf.c pulsecore/strbuf.h \ -		pulsecore/strlist.c pulsecore/strlist.h \ -		pulsecore/tagstruct.c pulsecore/tagstruct.h \ -		pulsecore/core-error.c pulsecore/core-error.h \ -		pulsecore/winsock.h pulsecore/creds.h \ -		pulsecore/shm.c pulsecore/shm.h \ -		pulsecore/flist.c pulsecore/flist.h \ -		pulsecore/object.c pulsecore/object.h \ -		pulsecore/msgobject.c pulsecore/msgobject.h \ -		pulsecore/once.c pulsecore/once.h \ -		pulsecore/rtclock.c pulsecore/rtclock.h \ -		pulsecore/time-smoother.c pulsecore/time-smoother.h \ -		pulsecore/proplist-util.c pulsecore/proplist-util.h \ -		$(PA_THREAD_OBJS) - -if OS_IS_WIN32 -libpulse_la_SOURCES += \ -		pulsecore/dllmain.c -endif - -if HAVE_X11 -libpulse_la_SOURCES += \ -		pulse/client-conf-x11.c pulse/client-conf-x11.h \ -		pulsecore/x11prop.c pulsecore/x11prop.h -endif +		pulse/xmalloc.c pulse/xmalloc.h  libpulse_la_CFLAGS = $(AM_CFLAGS) -libpulse_la_LDFLAGS = -version-info $(LIBPULSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file -libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) +libpulse_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file +libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) libpulsecommon-@PA_MAJORMINORMICRO@.la  if HAVE_X11 +libpulse_la_SOURCES += pulse/client-conf-x11.c pulse/client-conf-x11.h  libpulse_la_CFLAGS += $(X_CFLAGS)  libpulse_la_LDFLAGS += $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)  endif -if HAVE_LIBASYNCNS -libpulse_la_CFLAGS += $(LIBASYNCNS_CFLAGS) -libpulse_la_LIBADD += $(LIBASYNCNS_LIBS) -endif - -libpulse_simple_la_SOURCES = \ -		pulse/simple.c pulse/simple.h	\ -		pulsecore/log.c pulsecore/log.h \ -		pulsecore/core-util.c pulsecore/core-util.h  \ -		pulsecore/core-error.c pulsecore/core-error.h \ -		pulsecore/once.c pulsecore/once.h \ -		$(PA_THREAD_OBJS) - +libpulse_simple_la_SOURCES = pulse/simple.c pulse/simple.h  libpulse_simple_la_CFLAGS = $(AM_CFLAGS) -libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la -libpulse_simple_la_LDFLAGS = -version-info $(LIBPULSE_SIMPLE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file +libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la +libpulse_simple_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_SIMPLE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file -libpulse_browse_la_SOURCES = pulse/browser.c pulse/browser.h pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h  pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS) +libpulse_browse_la_SOURCES = pulse/browser.c pulse/browser.h pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h  libpulse_browse_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS) -libpulse_browse_la_LIBADD = $(AM_LIBADD) libpulse.la $(AVAHI_LIBS) -libpulse_browse_la_LDFLAGS = -version-info $(LIBPULSE_BROWSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file +libpulse_browse_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la $(AVAHI_LIBS) +libpulse_browse_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_BROWSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file -libpulse_mainloop_glib_la_SOURCES = \ -		pulse/glib-mainloop.h pulse/glib-mainloop.c \ -		pulsecore/log.c pulsecore/log.h \ -		pulsecore/core-util.c pulsecore/core-util.h  \ -		pulsecore/core-error.c pulsecore/core-error.h \ -		pulsecore/once.c pulsecore/once.h \ -		$(PA_THREAD_OBJS) +libpulse_mainloop_glib_la_SOURCES = pulse/glib-mainloop.h pulse/glib-mainloop.c  libpulse_mainloop_glib_la_CFLAGS = $(AM_CFLAGS) $(GLIB20_CFLAGS) -libpulse_mainloop_glib_la_LIBADD = $(AM_LIBADD) libpulse.la $(GLIB20_LIBS) -libpulse_mainloop_glib_la_LDFLAGS = -version-info $(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file +libpulse_mainloop_glib_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la $(GLIB20_LIBS) +libpulse_mainloop_glib_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file  ###################################  #         OSS emulation           #  ###################################  if HAVE_OSS -  lib_LTLIBRARIES += libpulsedsp.la -  bin_SCRIPTS += utils/padsp -  endif -libpulsedsp_la_SOURCES = utils/padsp.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h  pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS) +libpulsedsp_la_SOURCES = utils/padsp.c  libpulsedsp_la_CFLAGS = $(AM_CFLAGS) -libpulsedsp_la_LIBADD = $(AM_LIBADD) libpulse.la -libpulsedsp_la_LDFLAGS = -avoid-version - -################################### -#      ffmpeg resampler           # -################################### - -noinst_LTLIBRARIES = libffmpeg-resampler.la - -libffmpeg_resampler_la_CPPFLAGS = $(AM_CPPFLAGS) -libffmpeg_resampler_la_SOURCES = pulsecore/ffmpeg/resample2.c pulsecore/ffmpeg/avcodec.h pulsecore/ffmpeg/dsputil.h +libpulsedsp_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la +libpulsedsp_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version  ###################################  #      Daemon core library        #  ################################### -#pulsecoreinclude_HEADERS = -noinst_HEADERS = \ -		pulsecore/autoload.h \ -		pulsecore/atomic.h \ -		pulsecore/cli-command.h \ -		pulsecore/cli-text.h \ -		pulsecore/client.h \ -		pulsecore/core.h \ -		pulsecore/core-scache.h \ -		pulsecore/core-subscribe.h \ -		pulsecore/conf-parser.h \ -		pulsecore/core-util.h \ -		pulsecore/dynarray.h \ -		pulsecore/g711.h \ -		pulsecore/hashmap.h \ -		pulsecore/idxset.h \ -		pulsecore/log.h \ -		pulsecore/mcalign.h \ -		pulsecore/memblock.h \ -		pulsecore/memblockq.h \ -		pulsecore/memchunk.h \ -		pulsecore/modargs.h \ -		pulsecore/modinfo.h \ -		pulsecore/module.h \ -		pulsecore/namereg.h \ -		pulsecore/pid.h \ -		pulsecore/play-memchunk.h \ -		pulsecore/play-memblockq.h \ -		pulsecore/shared.h \ -		pulsecore/queue.h \ -		pulsecore/random.h \ -		pulsecore/resampler.h \ -		pulsecore/sample-util.h \ -		pulsecore/sconv.h \ -		pulsecore/sink.h \ -		pulsecore/sink-input.h \ -		pulsecore/sioman.h \ -		pulsecore/sound-file.h \ -		pulsecore/sound-file-stream.h \ -		pulsecore/source.h \ -		pulsecore/source-output.h \ -		pulsecore/strbuf.h \ -		pulsecore/tokenizer.h \ -		pulsecore/creds.h \ -		pulsecore/shm.h \ -		pulsecore/llist.h \ -		pulsecore/refcnt.h \ -		pulsecore/mutex.h \ -		pulsecore/thread.h \ -		pulsecore/semaphore.h \ -		pulsecore/once.h - -lib_LTLIBRARIES += libpulsecore.la - -# Some public stuff is used even in the core -libpulsecore_la_SOURCES = \ -		pulse/channelmap.c pulse/channelmap.h \ -		pulse/error.c pulse/error.h \ -		pulse/mainloop.c pulse/mainloop.h \ -		pulse/mainloop-api.c pulse/mainloop-api.h \ -		pulse/mainloop-signal.c pulse/mainloop-signal.h \ -		pulse/sample.c pulse/sample.h \ -		pulse/timeval.c pulse/timeval.h \ -		pulse/utf8.c pulse/utf8.h \ -		pulse/util.c pulse/util.h \ -		pulse/volume.c pulse/volume.h \ -		pulse/xmalloc.c pulse/xmalloc.h \ -		pulse/proplist.c pulse/proplist.h \ -		pulse/i18n.c pulse/i18n.h +lib_LTLIBRARIES += libpulsecore-@PA_MAJORMINORMICRO@.la -# Pure core stuff (some are shared in libpulse though). -libpulsecore_la_SOURCES += \ +# Pure core stuff +libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \ +		pulsecore/asyncmsgq.c pulsecore/asyncmsgq.h \ +		pulsecore/asyncq.c pulsecore/asyncq.h \ +		pulsecore/auth-cookie.c pulsecore/auth-cookie.h \  		pulsecore/autoload.c pulsecore/autoload.h \  		pulsecore/cli-command.c pulsecore/cli-command.h \  		pulsecore/cli-text.c pulsecore/cli-text.h \  		pulsecore/client.c pulsecore/client.h \ -		pulsecore/conf-parser.c pulsecore/conf-parser.h \ -		pulsecore/core.c pulsecore/core.h \  		pulsecore/core-scache.c pulsecore/core-scache.h \  		pulsecore/core-subscribe.c pulsecore/core-subscribe.h \ -		pulsecore/core-util.c pulsecore/core-util.h \ -		pulsecore/dynarray.c pulsecore/dynarray.h \ -		pulsecore/endianmacros.h \ +		pulsecore/core.c pulsecore/core.h \ +		pulsecore/envelope.c pulsecore/envelope.h \ +		pulsecore/fdsem.c pulsecore/fdsem.h \ +		pulsecore/ffmpeg/resample2.c pulsecore/ffmpeg/avcodec.h pulsecore/ffmpeg/dsputil.h \  		pulsecore/g711.c pulsecore/g711.h \ -		pulsecore/hashmap.c pulsecore/hashmap.h \ -		pulsecore/idxset.c pulsecore/idxset.h \ -		pulsecore/log.c pulsecore/log.h \ -		pulsecore/mcalign.c pulsecore/mcalign.h \ -		pulsecore/memblock.c pulsecore/memblock.h \ -		pulsecore/memblockq.c pulsecore/memblockq.h \ -		pulsecore/memchunk.c pulsecore/memchunk.h \ +		pulsecore/hook-list.c pulsecore/hook-list.h \ +		pulsecore/ltdl-helper.c pulsecore/ltdl-helper.h \  		pulsecore/modargs.c pulsecore/modargs.h \  		pulsecore/modinfo.c pulsecore/modinfo.h \ -		pulsecore/ltdl-helper.c pulsecore/ltdl-helper.h \  		pulsecore/module.c pulsecore/module.h \ +		pulsecore/msgobject.c pulsecore/msgobject.h \  		pulsecore/namereg.c pulsecore/namereg.h \ -		pulsecore/pid.c pulsecore/pid.h \ -		pulsecore/pipe.c pulsecore/pipe.h \ -		pulsecore/play-memchunk.c pulsecore/play-memchunk.h \ +		pulsecore/object.c pulsecore/object.h \  		pulsecore/play-memblockq.c pulsecore/play-memblockq.h \ -		pulsecore/poll.c pulsecore/poll.h \ -		pulsecore/shared.c pulsecore/shared.h \ -		pulsecore/queue.c pulsecore/queue.h \ -		pulsecore/random.c pulsecore/random.h \ +		pulsecore/play-memchunk.c pulsecore/play-memchunk.h \  		pulsecore/resampler.c pulsecore/resampler.h \ +		pulsecore/rtpoll.c pulsecore/rtpoll.h \ +		pulsecore/rtsig.c pulsecore/rtsig.h \  		pulsecore/sample-util.c pulsecore/sample-util.h \ -		pulsecore/sconv.c pulsecore/sconv.h \  		pulsecore/sconv-s16be.c pulsecore/sconv-s16be.h \  		pulsecore/sconv-s16le.c pulsecore/sconv-s16le.h \ -		pulsecore/sink.c pulsecore/sink.h \ +		pulsecore/sconv.c pulsecore/sconv.h \ +		pulsecore/shared.c pulsecore/shared.h \ +		pulsecore/shm.c pulsecore/shm.h \  		pulsecore/sink-input.c pulsecore/sink-input.h \ +		pulsecore/sink.c pulsecore/sink.h \  		pulsecore/sioman.c pulsecore/sioman.h \ -		pulsecore/sound-file.c pulsecore/sound-file.h \  		pulsecore/sound-file-stream.c pulsecore/sound-file-stream.h \ -		pulsecore/source.c pulsecore/source.h \ +		pulsecore/sound-file.c pulsecore/sound-file.h \  		pulsecore/source-output.c pulsecore/source-output.h \ -		pulsecore/strbuf.c pulsecore/strbuf.h \ -		pulsecore/tokenizer.c pulsecore/tokenizer.h \ -		pulsecore/winsock.h \ -		pulsecore/core-error.c pulsecore/core-error.h \ -		pulsecore/hook-list.c pulsecore/hook-list.h \ -		pulsecore/shm.c pulsecore/shm.h \ -		pulsecore/flist.c pulsecore/flist.h \ -		pulsecore/asyncmsgq.c pulsecore/asyncmsgq.h \ -		pulsecore/asyncq.c pulsecore/asyncq.h \ -		pulsecore/thread-mq.c pulsecore/thread-mq.h \ -		pulsecore/fdsem.c pulsecore/fdsem.h \ -		pulsecore/object.c pulsecore/object.h \ -		pulsecore/msgobject.c pulsecore/msgobject.h \ -		pulsecore/rtsig.c pulsecore/rtsig.h \ -		pulsecore/rtpoll.c pulsecore/rtpoll.h \ -		pulsecore/rtclock.c pulsecore/rtclock.h \ -		pulsecore/macro.h \ -		pulsecore/once.c pulsecore/once.h \ -		pulsecore/time-smoother.c pulsecore/time-smoother.h \ +		pulsecore/source.c pulsecore/source.h \  		pulsecore/start-child.c pulsecore/start-child.h \ -		pulsecore/envelope.c pulsecore/envelope.h \ -		pulsecore/proplist-util.c pulsecore/proplist-util.h \ -		$(PA_THREAD_OBJS) +		pulsecore/thread-mq.c pulsecore/thread-mq.h \ +		pulsecore/time-smoother.c pulsecore/time-smoother.h -if OS_IS_WIN32 -libpulsecore_la_SOURCES += \ -		pulsecore/dllmain.c -endif +libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) +libpulsecore_@PA_MAJORMINORMICRO@_la_LDFLAGS = -avoid-version +libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(LIBSPEEX_LIBS) $(WINSOCK_LIBS) $(LIBOIL_LIBS) $(LTLIBICONV) libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la -libpulsecore_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBOIL_CFLAGS) -libpulsecore_la_LDFLAGS = -version-info $(LIBPULSECORE_VERSION_INFO) -libpulsecore_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(LIBSPEEX_LIBS) $(WINSOCK_LIBS) $(LIBOIL_LIBS) $(LTLIBICONV) libffmpeg-resampler.la +if HAVE_X11 +libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/x11wrap.c pulsecore/x11wrap.h +libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(X_CFLAGS) +libpulsecore_@PA_MAJORMINORMICRO@_la_LDFLAGS += $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) +endif  ###################################  #   Plug-in support libraries     #  ################################### -#pulsecoreinclude_HEADERS += -noinst_HEADERS += \ -		pulsecore/socket-util.h \ -		pulsecore/iochannel.h \ -		pulsecore/socket-server.h \ -		pulsecore/ipacl.h \ -		pulsecore/socket-client.h \ -		pulsecore/parseaddr.h \ -		pulsecore/packet.h \ -		pulsecore/pstream.h \ -		pulsecore/ioline.h \ -		pulsecore/cli.h \ -		pulsecore/protocol-cli.h \ -		pulsecore/tagstruct.h \ -		pulsecore/pstream-util.h \ -		pulsecore/pdispatch.h \ -		pulsecore/authkey.h \ -		pulsecore/auth-cookie.h \ -		pulsecore/strlist.h \ -		pulsecore/protocol-simple.h \ -		pulsecore/esound.h \ -		pulsecore/protocol-esound.h \ -		pulsecore/native-common.h \ -		pulsecore/protocol-native.h \ -		pulsecore/protocol-http.h -  ### Warning! Due to an obscure bug in libtool/automake it is required  ### that the libraries in modlibexec_LTLIBRARIES are specified in-order,  ### i.e. libraries near the end of the list depend on libraries near  ### the head, and not the other way!  modlibexec_LTLIBRARIES = \ -		libsocket-util.la \ -		libiochannel.la \ -		libsocket-server.la \ -		libipacl.la \ -		libparseaddr.la \ -		libsocket-client.la \ -		libpacket.la \ -		libpstream.la \ -		libioline.la \  		libcli.la \  		libprotocol-cli.la \ -		libtagstruct.la \ -		libpstream-util.la \ -		libpdispatch.la \ -		libauthkey.la \ -		libauth-cookie.la \ -		libstrlist.la \  		libprotocol-simple.la \  		libprotocol-http.la \  		libprotocol-native.la \ @@ -876,146 +748,63 @@ modlibexec_LTLIBRARIES += \  		librtp.la  endif -if HAVE_X11 -#pulsecoreinclude_HEADERS += -noinst_HEADERS += \ -		pulsecore/x11wrap.h \ -		pulsecore/x11prop.h - -modlibexec_LTLIBRARIES += \ -		libx11wrap.la \ -		libx11prop.la -endif -  if HAVE_AVAHI -#pulsecoreinclude_HEADERS += -noinst_HEADERS += \ -		pulsecore/avahi-wrap.h -  modlibexec_LTLIBRARIES += \  		libavahi-wrap.la  endif  libprotocol_simple_la_SOURCES = pulsecore/protocol-simple.c pulsecore/protocol-simple.h  libprotocol_simple_la_LDFLAGS = -avoid-version -libprotocol_simple_la_LIBADD = $(AM_LIBADD) libpulsecore.la libsocket-server.la libiochannel.la - -libsocket_server_la_SOURCES = \ -		pulsecore/inet_ntop.c pulsecore/inet_ntop.h \ -		pulsecore/inet_pton.c pulsecore/inet_pton.h \ -		pulsecore/socket-server.c pulsecore/socket-server.h -libsocket_server_la_LDFLAGS = -avoid-version -libsocket_server_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-util.la $(LIBWRAP_LIBS) $(WINSOCK_LIBS) - -libipacl_la_SOURCES = pulsecore/ipacl.h pulsecore/ipacl.c \ -		pulsecore/inet_pton.c pulsecore/inet_pton.h -libipacl_la_LDFLAGS = -avoid-version -libipacl_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(WINSOCK_LIBS) - -libsocket_client_la_SOURCES = pulsecore/socket-client.c pulsecore/socket-client.h -libsocket_client_la_LDFLAGS = -avoid-version -libsocket_client_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-util.la libparseaddr.la $(LIBASYNCNS_LIBS) $(WINSOCK_LIBS) -libsocket_client_la_CFLAGS = $(AM_CFLAGS) $(LIBASYNCNS_CFLAGS) - -libparseaddr_la_SOURCES = pulsecore/parseaddr.c pulsecore/parseaddr.h -libparseaddr_la_LDFLAGS = -avoid-version -libparseaddr_la_LIBADD = $(AM_LIBADD) libpulsecore.la - -libpstream_la_SOURCES = pulsecore/pstream.c pulsecore/pstream.h -libpstream_la_LDFLAGS = -avoid-version -libpstream_la_LIBADD = $(AM_LIBADD) libpulsecore.la libpacket.la libiochannel.la $(WINSOCK_LIBS) - -libpstream_util_la_SOURCES = pulsecore/pstream-util.c pulsecore/pstream-util.h -libpstream_util_la_LDFLAGS = -avoid-version -libpstream_util_la_LIBADD = $(AM_LIBADD) libpacket.la libpstream.la libtagstruct.la libpulsecore.la - -libpdispatch_la_SOURCES = pulsecore/pdispatch.c pulsecore/pdispatch.h -libpdispatch_la_LDFLAGS = -avoid-version -libpdispatch_la_LIBADD = $(AM_LIBADD) libtagstruct.la libpulsecore.la - -libiochannel_la_SOURCES = pulsecore/iochannel.c pulsecore/iochannel.h -libiochannel_la_LDFLAGS = -avoid-version -libiochannel_la_LIBADD = $(AM_LIBADD) libsocket-util.la libpulsecore.la $(WINSOCK_LIBS) - -libpacket_la_SOURCES = pulsecore/packet.c pulsecore/packet.h -libpacket_la_LDFLAGS = -avoid-version -libpacket_la_LIBADD = $(AM_LIBADD) libpulsecore.la - -libioline_la_SOURCES = pulsecore/ioline.c pulsecore/ioline.h -libioline_la_LDFLAGS = -avoid-version -libioline_la_LIBADD = $(AM_LIBADD) libiochannel.la libpulsecore.la +libprotocol_simple_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  libcli_la_SOURCES = pulsecore/cli.c pulsecore/cli.h -libcli_la_CPPFLAGS = $(AM_CPPFLAGS)  libcli_la_LDFLAGS = -avoid-version -libcli_la_LIBADD = $(AM_LIBADD) libiochannel.la libioline.la libpulsecore.la - -libstrlist_la_SOURCES = pulsecore/strlist.c pulsecore/strlist.h -libstrlist_la_LDFLAGS = -avoid-version -libstrlist_la_LIBADD = $(AM_LIBADD) libpulsecore.la +libcli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  libprotocol_cli_la_SOURCES = pulsecore/protocol-cli.c pulsecore/protocol-cli.h  libprotocol_cli_la_LDFLAGS = -avoid-version -libprotocol_cli_la_LIBADD = $(AM_LIBADD) libsocket-server.la libiochannel.la libcli.la libpulsecore.la +libprotocol_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  libprotocol_http_la_SOURCES = pulsecore/protocol-http.c pulsecore/protocol-http.h  libprotocol_http_la_LDFLAGS = -avoid-version -libprotocol_http_la_LIBADD = $(AM_LIBADD) libsocket-server.la libioline.la libpulsecore.la libiochannel.la +libprotocol_http_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  libprotocol_native_la_SOURCES = pulsecore/protocol-native.c pulsecore/protocol-native.h pulsecore/native-common.h  libprotocol_native_la_LDFLAGS = -avoid-version -libprotocol_native_la_LIBADD = $(AM_LIBADD) libsocket-server.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauth-cookie.la libstrlist.la libpulsecore.la libiochannel.la libipacl.la - -libtagstruct_la_SOURCES = pulsecore/tagstruct.c pulsecore/tagstruct.h -libtagstruct_la_LDFLAGS = -avoid-version -libtagstruct_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(WINSOCK_LIBS) +libprotocol_native_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  libprotocol_esound_la_SOURCES = pulsecore/protocol-esound.c pulsecore/protocol-esound.h pulsecore/esound.h  libprotocol_esound_la_LDFLAGS = -avoid-version -libprotocol_esound_la_LIBADD = $(AM_LIBADD) libsocket-server.la libiochannel.la libauthkey.la libauth-cookie.la libpulsecore.la libipacl.la - -libauthkey_la_SOURCES = pulsecore/authkey.c pulsecore/authkey.h -libauthkey_la_LDFLAGS = -avoid-version -libauthkey_la_LIBADD = $(AM_LIBADD) libpulsecore.la - -libauth_cookie_la_SOURCES = pulsecore/auth-cookie.c pulsecore/auth-cookie.h -libauth_cookie_la_LDFLAGS = -avoid-version -libauth_cookie_la_LIBADD = $(AM_LIBADD) libauthkey.la libpulsecore.la - -libsocket_util_la_SOURCES = \ -		pulsecore/inet_ntop.c pulsecore/inet_ntop.h \ -		pulsecore/socket-util.c pulsecore/socket-util.h -libsocket_util_la_LDFLAGS = -avoid-version -libsocket_util_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) libpulsecore.la - -librtp_la_SOURCES = modules/rtp/rtp.c modules/rtp/rtp.h modules/rtp/sdp.c modules/rtp/sdp.h modules/rtp/sap.c modules/rtp/sap.h +libprotocol_esound_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la + +librtp_la_SOURCES = \ +		modules/rtp/rtp.c modules/rtp/rtp.h \ +		modules/rtp/sdp.c modules/rtp/sdp.h \ +		modules/rtp/sap.c modules/rtp/sap.h \ +		modules/rtp/rtsp_client.c modules/rtp/rtsp_client.h \ +		modules/rtp/headerlist.c modules/rtp/headerlist.h  librtp_la_LDFLAGS = -avoid-version -librtp_la_LIBADD = $(AM_LIBADD) libpulsecore.la +librtp_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la -# X11 - -libx11wrap_la_SOURCES = pulsecore/x11wrap.c pulsecore/x11wrap.h -libx11wrap_la_LDFLAGS = -avoid-version -libx11wrap_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS) -libx11wrap_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libpulsecore.la - -libx11prop_la_SOURCES = pulsecore/x11prop.c pulsecore/x11prop.h -libx11prop_la_LDFLAGS = -avoid-version -libx11prop_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS) -libx11prop_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) +libraop_la_SOURCES = \ +        modules/raop/raop_client.c modules/raop/raop_client.h \ +        modules/raop/base64.c modules/raop/base64.h +libraop_la_CFLAGS = $(AM_CFLAGS) $(OPENSSL_CFLAGS) +libraop_la_LDFLAGS = -avoid-version +libraop_la_LIBADD = $(AM_LIBADD) $(OPENSSL_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la librtp.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  # Avahi -  libavahi_wrap_la_SOURCES = pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h  libavahi_wrap_la_LDFLAGS = -avoid-version  libavahi_wrap_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS) -libavahi_wrap_la_LIBADD = $(AM_LIBADD) $(AVAHI_CFLAGS) libpulsecore.la +libavahi_wrap_la_LIBADD = $(AM_LIBADD) $(AVAHI_CFLAGS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  ###################################  #        Plug-in libraries        #  ###################################  modlibexec_LTLIBRARIES += \ +		module-flat-volume.la \  		module-cli.la \  		module-cli-protocol-tcp.la \  		module-simple-protocol-tcp.la \ @@ -1151,12 +940,27 @@ endif  if HAVE_BLUEZ  modlibexec_LTLIBRARIES += \ -		module-bt-proximity.la +		module-bluetooth-proximity.la \ +		module-bluetooth-discover.la \ +		libbluetooth-ipc.la \ +		libbluetooth-sbc.la \ +		module-bluetooth-device.la  pulselibexec_PROGRAMS += \ -		bt-proximity-helper +		proximity-helper  endif +if HAVE_OPENSSL +modlibexec_LTLIBRARIES += \ +		libraop.la \ +		module-raop-sink.la +if HAVE_AVAHI +modlibexec_LTLIBRARIES += \ +		module-raop-discover.la +endif +endif + +  # These are generated by a M4 script  SYMDEF_FILES = \ @@ -1210,10 +1014,15 @@ SYMDEF_FILES = \  		modules/module-rescue-streams-symdef.h \  		modules/module-suspend-on-idle-symdef.h \  		modules/module-hal-detect-symdef.h \ -		modules/module-bt-proximity-symdef.h \ +		modules/bluetooth/module-bluetooth-proximity-symdef.h \ +		modules/bluetooth/module-bluetooth-discover-symdef.h \ +		modules/bluetooth/module-bluetooth-device-symdef.h \ +		modules/module-raop-sink-symdef.h \ +		modules/module-raop-discover-symdef.h \  		modules/gconf/module-gconf-symdef.h \  		modules/module-position-event-sounds-symdef.h \ -		modules/module-console-kit-symdef.h +		modules/module-console-kit-symdef.h \ +		modules/module-flat-volume-symdef.h  EXTRA_DIST += $(SYMDEF_FILES)  BUILT_SOURCES += $(SYMDEF_FILES) @@ -1222,334 +1031,373 @@ $(SYMDEF_FILES): modules/module-defs.h.m4  	$(MKDIR_P) modules  	$(MKDIR_P) modules/gconf  	$(MKDIR_P) modules/rtp +	$(MKDIR_P) modules/bluetooth  	$(M4) -Dfname="$@" $< > $@ +# Flat volume + +module_flat_volume_la_SOURCES = modules/module-flat-volume.c +module_flat_volume_la_LDFLAGS = -module -avoid-version +module_flat_volume_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +  # Simple protocol  module_simple_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c  module_simple_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)  module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version -module_simple_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-simple.la libsocket-server.la +module_simple_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-simple.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_simple_protocol_unix_la_SOURCES = modules/module-protocol-stub.c  module_simple_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)  module_simple_protocol_unix_la_LDFLAGS = -module -avoid-version -module_simple_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-simple.la libsocket-server.la libsocket-util.la +module_simple_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-simple.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  # CLI protocol  module_cli_la_SOURCES = modules/module-cli.c  module_cli_la_LDFLAGS = -module -avoid-version -module_cli_la_LIBADD = $(AM_LIBADD) libcli.la libiochannel.la libpulsecore.la +module_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libcli.la  module_cli_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c  module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)  module_cli_protocol_tcp_la_LDFLAGS = -module -avoid-version -module_cli_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-cli.la libsocket-server.la +module_cli_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-cli.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_cli_protocol_unix_la_SOURCES = modules/module-protocol-stub.c  module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)  module_cli_protocol_unix_la_LDFLAGS = -module -avoid-version -module_cli_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-cli.la libsocket-server.la libsocket-util.la +module_cli_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-cli.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  # HTTP protocol  module_http_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c  module_http_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS)  module_http_protocol_tcp_la_LDFLAGS = -module -avoid-version -module_http_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-http.la libsocket-server.la +module_http_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-http.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_http_protocol_unix_la_SOURCES = modules/module-protocol-stub.c  module_http_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS)  module_http_protocol_unix_la_LDFLAGS = -module -avoid-version -module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-http.la libsocket-server.la libsocket-util.la +module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-http.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  # Native protocol  module_native_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c  module_native_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)  module_native_protocol_tcp_la_LDFLAGS = -module -avoid-version -module_native_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-native.la libsocket-server.la +module_native_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-native.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_native_protocol_unix_la_SOURCES = modules/module-protocol-stub.c  module_native_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)  module_native_protocol_unix_la_LDFLAGS = -module -avoid-version -module_native_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-native.la libsocket-server.la libsocket-util.la +module_native_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-native.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_native_protocol_fd_la_SOURCES = modules/module-native-protocol-fd.c  module_native_protocol_fd_la_CFLAGS = $(AM_CFLAGS)  module_native_protocol_fd_la_LDFLAGS = -module -avoid-version -module_native_protocol_fd_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-native.la libsocket-server.la libsocket-util.la libiochannel.la +module_native_protocol_fd_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-native.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  # EsounD protocol  module_esound_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c  module_esound_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)  module_esound_protocol_tcp_la_LDFLAGS = -module -avoid-version -module_esound_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-esound.la libsocket-server.la +module_esound_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-esound.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_esound_protocol_unix_la_SOURCES = modules/module-protocol-stub.c  module_esound_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)  module_esound_protocol_unix_la_LDFLAGS = -module -avoid-version -module_esound_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-esound.la libsocket-server.la libsocket-util.la +module_esound_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-esound.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_esound_compat_spawnfd_la_SOURCES = modules/module-esound-compat-spawnfd.c  module_esound_compat_spawnfd_la_LDFLAGS = -module -avoid-version -module_esound_compat_spawnfd_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_esound_compat_spawnfd_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_esound_compat_spawnpid_la_SOURCES = modules/module-esound-compat-spawnpid.c  module_esound_compat_spawnpid_la_LDFLAGS = -module -avoid-version -module_esound_compat_spawnpid_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_esound_compat_spawnpid_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_esound_sink_la_SOURCES = modules/module-esound-sink.c  module_esound_sink_la_LDFLAGS = -module -avoid-version -module_esound_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-client.la libauthkey.la libsocket-util.la +module_esound_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  # Pipes  module_pipe_sink_la_SOURCES = modules/module-pipe-sink.c  module_pipe_sink_la_LDFLAGS = -module -avoid-version -module_pipe_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la +module_pipe_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_pipe_source_la_SOURCES = modules/module-pipe-source.c  module_pipe_source_la_LDFLAGS = -module -avoid-version -module_pipe_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la +module_pipe_source_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  # Fake sources/sinks  module_sine_la_SOURCES = modules/module-sine.c  module_sine_la_LDFLAGS = -module -avoid-version -module_sine_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_sine_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_null_sink_la_SOURCES = modules/module-null-sink.c  module_null_sink_la_LDFLAGS = -module -avoid-version -module_null_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_null_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  # Couplings  module_combine_la_SOURCES = modules/module-combine.c  module_combine_la_LDFLAGS = -module -avoid-version -module_combine_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_combine_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_remap_sink_la_SOURCES = modules/module-remap-sink.c  module_remap_sink_la_LDFLAGS = -module -avoid-version -module_remap_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_remap_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_ladspa_sink_la_SOURCES = modules/module-ladspa-sink.c modules/ladspa.h  module_ladspa_sink_la_CFLAGS = -DLADSPA_PATH=\"$(libdir)/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/local/lib64/ladspa:/usr/lib64/ladspa\" $(AM_CFLAGS)  module_ladspa_sink_la_LDFLAGS = -module -avoid-version -module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore.la +module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_match_la_SOURCES = modules/module-match.c  module_match_la_LDFLAGS = -module -avoid-version -module_match_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_match_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_tunnel_sink_la_SOURCES = modules/module-tunnel.c  module_tunnel_sink_la_CFLAGS = -DTUNNEL_SINK=1 $(AM_CFLAGS)  module_tunnel_sink_la_LDFLAGS = -module -avoid-version -module_tunnel_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauth-cookie.la libsocket-util.la libiochannel.la +module_tunnel_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_tunnel_source_la_SOURCES = modules/module-tunnel.c  module_tunnel_source_la_LDFLAGS = -module -avoid-version -module_tunnel_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauth-cookie.la libsocket-util.la libiochannel.la +module_tunnel_source_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  # X11  module_x11_bell_la_SOURCES = modules/module-x11-bell.c  module_x11_bell_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)  module_x11_bell_la_LDFLAGS = -module -avoid-version -module_x11_bell_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libpulsecore.la +module_x11_bell_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_x11_publish_la_SOURCES = modules/module-x11-publish.c  module_x11_publish_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)  module_x11_publish_la_LDFLAGS = -module -avoid-version -module_x11_publish_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libauthkey.la libauth-cookie.la libx11prop.la libstrlist.la libpulsecore.la +module_x11_publish_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libprotocol-native.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_x11_xsmp_la_SOURCES = modules/module-x11-xsmp.c  module_x11_xsmp_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)  module_x11_xsmp_la_LDFLAGS = -module -avoid-version -module_x11_xsmp_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libpulsecore.la +module_x11_xsmp_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  # OSS  liboss_util_la_SOURCES = modules/oss-util.c modules/oss-util.h  liboss_util_la_LDFLAGS = -avoid-version -liboss_util_la_LIBADD = libpulsecore.la +liboss_util_la_LIBADD = libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_oss_la_SOURCES = modules/module-oss.c  module_oss_la_LDFLAGS = -module -avoid-version -module_oss_la_LIBADD = $(AM_LIBADD) libiochannel.la liboss-util.la libpulsecore.la +module_oss_la_LIBADD = $(AM_LIBADD) liboss-util.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  # ALSA  libalsa_util_la_SOURCES = modules/alsa-util.c modules/alsa-util.h  libalsa_util_la_LDFLAGS = -avoid-version -libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libpulsecore.la +libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)  module_alsa_sink_la_SOURCES = modules/module-alsa-sink.c  module_alsa_sink_la_LDFLAGS = -module -avoid-version -module_alsa_sink_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore.la +module_alsa_sink_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_alsa_sink_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)  module_alsa_source_la_SOURCES = modules/module-alsa-source.c  module_alsa_source_la_LDFLAGS = -module -avoid-version -module_alsa_source_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore.la +module_alsa_source_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)  # Solaris  module_solaris_la_SOURCES = modules/module-solaris.c  module_solaris_la_LDFLAGS = -module -avoid-version -module_solaris_la_LIBADD = $(AM_LIBADD) libiochannel.la libpulsecore.la +module_solaris_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  # Avahi  module_zeroconf_publish_la_SOURCES = modules/module-zeroconf-publish.c  module_zeroconf_publish_la_LDFLAGS = -module -avoid-version -module_zeroconf_publish_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore.la +module_zeroconf_publish_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_zeroconf_publish_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)  module_zeroconf_discover_la_SOURCES = modules/module-zeroconf-discover.c  module_zeroconf_discover_la_LDFLAGS = -module -avoid-version -module_zeroconf_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore.la +module_zeroconf_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_zeroconf_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)  # LIRC  module_lirc_la_SOURCES = modules/module-lirc.c  module_lirc_la_LDFLAGS = -module -avoid-version -module_lirc_la_LIBADD = $(AM_LIBADD) $(LIRC_LIBS) libpulsecore.la +module_lirc_la_LIBADD = $(AM_LIBADD) $(LIRC_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_lirc_la_CFLAGS = $(AM_CFLAGS) $(LIRC_CFLAGS)  # Linux evdev  module_mmkbd_evdev_la_SOURCES = modules/module-mmkbd-evdev.c  module_mmkbd_evdev_la_LDFLAGS = -module -avoid-version -module_mmkbd_evdev_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_mmkbd_evdev_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_mmkbd_evdev_la_CFLAGS = $(AM_CFLAGS)  # Windows waveout  #module_waveout_la_SOURCES = modules/module-waveout.c  #module_waveout_la_LDFLAGS = -module -avoid-version -#module_waveout_la_LIBADD = $(AM_LIBADD) libpulsecore.la -lwinmm +#module_waveout_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la -lwinmm libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  #module_waveout_la_CFLAGS = $(AM_CFLAGS)  # Hardware autodetection module  module_detect_la_SOURCES = modules/module-detect.c  module_detect_la_LDFLAGS = -module -avoid-version -module_detect_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_detect_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_detect_la_CFLAGS = $(AM_CFLAGS)  # Volume restore module  module_volume_restore_la_SOURCES = modules/module-volume-restore.c  module_volume_restore_la_LDFLAGS = -module -avoid-version -module_volume_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_volume_restore_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_volume_restore_la_CFLAGS = $(AM_CFLAGS)  # Position event sounds in space  module_position_event_sounds_la_SOURCES = modules/module-position-event-sounds.c  module_position_event_sounds_la_LDFLAGS = -module -avoid-version -module_position_event_sounds_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_position_event_sounds_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_position_event_sounds_CFLAGS = $(AM_CFLAGS)  # Device volume/muted restore module  module_device_restore_la_SOURCES = modules/module-device-restore.c  module_device_restore_la_LDFLAGS = -module -avoid-version -module_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la -lgdbm +module_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la -lgdbm libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_device_restore_la_CFLAGS = $(AM_CFLAGS)  # Stream volume/muted/device restore module  module_stream_restore_la_SOURCES = modules/module-stream-restore.c  module_stream_restore_la_LDFLAGS = -module -avoid-version -module_stream_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la -lgdbm +module_stream_restore_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libpulsecore-@PA_MAJORMINORMICRO@.la -lgdbm libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_stream_restore_la_CFLAGS = $(AM_CFLAGS)  # Default sink/source restore module  module_default_device_restore_la_SOURCES = modules/module-default-device-restore.c  module_default_device_restore_la_LDFLAGS = -module -avoid-version -module_default_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_default_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_default_device_restore_la_CFLAGS = $(AM_CFLAGS)  # Always Sink module  module_always_sink_la_SOURCES = modules/module-always-sink.c  module_always_sink_la_LDFLAGS = -module -avoid-version -module_always_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_always_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_always_sink_la_CFLAGS = $(AM_CFLAGS)  # Rescue streams module  module_rescue_streams_la_SOURCES = modules/module-rescue-streams.c  module_rescue_streams_la_LDFLAGS = -module -avoid-version -module_rescue_streams_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_rescue_streams_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_rescue_streams_la_CFLAGS = $(AM_CFLAGS)  # Suspend-on-idle module  module_suspend_on_idle_la_SOURCES = modules/module-suspend-on-idle.c  module_suspend_on_idle_la_LDFLAGS = -module -avoid-version -module_suspend_on_idle_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_suspend_on_idle_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_suspend_on_idle_la_CFLAGS = $(AM_CFLAGS)  # RTP modules  module_rtp_send_la_SOURCES = modules/rtp/module-rtp-send.c  module_rtp_send_la_LDFLAGS = -module -avoid-version -module_rtp_send_la_LIBADD = $(AM_LIBADD) libpulsecore.la librtp.la libsocket-util.la +module_rtp_send_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la librtp.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_rtp_send_la_CFLAGS = $(AM_CFLAGS)  module_rtp_recv_la_SOURCES = modules/rtp/module-rtp-recv.c  module_rtp_recv_la_LDFLAGS = -module -avoid-version -module_rtp_recv_la_LIBADD = $(AM_LIBADD) libpulsecore.la librtp.la +module_rtp_recv_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la librtp.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_rtp_recv_la_CFLAGS = $(AM_CFLAGS)  # JACK  module_jack_sink_la_SOURCES = modules/module-jack-sink.c  module_jack_sink_la_LDFLAGS = -module -avoid-version -module_jack_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(JACK_LIBS) +module_jack_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la $(JACK_LIBS) libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_jack_sink_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS)  module_jack_source_la_SOURCES = modules/module-jack-source.c  module_jack_source_la_LDFLAGS = -module -avoid-version -module_jack_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(JACK_LIBS) +module_jack_source_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la $(JACK_LIBS) libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_jack_source_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS)  # HAL/D-Bus  libdbus_util_la_SOURCES = modules/dbus-util.c modules/dbus-util.h  libdbus_util_la_LDFLAGS = -avoid-version -libdbus_util_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la +libdbus_util_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  libdbus_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)  module_hal_detect_la_SOURCES = modules/module-hal-detect.c  module_hal_detect_la_LDFLAGS = -module -avoid-version -module_hal_detect_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore.la libdbus-util.la +module_hal_detect_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_hal_detect_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)  module_console_kit_la_SOURCES = modules/module-console-kit.c  module_console_kit_la_LDFLAGS = -module -avoid-version -module_console_kit_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la +module_console_kit_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_console_kit_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)  # GConf support  module_gconf_la_SOURCES = modules/gconf/module-gconf.c  module_gconf_la_LDFLAGS = -module -avoid-version -module_gconf_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_gconf_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  module_gconf_la_CFLAGS = $(AM_CFLAGS) -DPA_GCONF_HELPER=\"$(pulselibexecdir)/gconf-helper\"  gconf_helper_SOURCES = modules/gconf/gconf-helper.c -gconf_helper_LDADD = $(AM_LDADD) $(GCONF_LIBS) libpulsecore.la +gconf_helper_LDADD = $(AM_LDADD) $(GCONF_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la  gconf_helper_CFLAGS = $(AM_CFLAGS) $(GCONF_CFLAGS)  gconf_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  # Bluetooth proximity -module_bt_proximity_la_SOURCES = modules/module-bt-proximity.c -module_bt_proximity_la_LDFLAGS = -module -avoid-version -module_bt_proximity_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la -module_bt_proximity_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DPA_BT_PROXIMITY_HELPER=\"$(pulselibexecdir)/bt-proximity-helper\" +module_bluetooth_proximity_la_SOURCES = modules/bluetooth/module-bluetooth-proximity.c +module_bluetooth_proximity_la_LDFLAGS = -module -avoid-version +module_bluetooth_proximity_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_bluetooth_proximity_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DPA_BT_PROXIMITY_HELPER=\"$(pulselibexecdir)/proximity-helper\" + +proximity_helper_SOURCES = modules/bluetooth/proximity-helper.c +proximity_helper_LDADD = $(AM_LDADD) $(BLUEZ_LIBS) +proximity_helper_CFLAGS = $(AM_CFLAGS) $(BLUEZ_CFLAGS) +proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +# Bluetooth sink / source +module_bluetooth_discover_la_SOURCES = modules/bluetooth/module-bluetooth-discover.c +module_bluetooth_discover_la_LDFLAGS = -module -avoid-version +module_bluetooth_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) + +libbluetooth_sbc_la_SOURCES = modules/bluetooth/sbc.c modules/bluetooth/sbc.h modules/bluetooth/sbc_tables.h modules/bluetooth/sbc_math.h +libbluetooth_sbc_la_LDFLAGS = -avoid-version +libbluetooth_sbc_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +libbluetooth_sbc_la_CFLAGS = $(AM_CFLAGS) + +libbluetooth_ipc_la_SOURCES = modules/bluetooth/ipc.c modules/bluetooth/ipc.h +libbluetooth_ipc_la_LDFLAGS = -avoid-version +libbluetooth_ipc_la_LIBADD = $(AM_LIBADD)libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +libbluetooth_ipc_la_CFLAGS = $(AM_CFLAGS) + +module_bluetooth_device_la_SOURCES = modules/bluetooth/module-bluetooth-device.c modules/bluetooth/rtp.h +module_bluetooth_device_la_LDFLAGS = -module -avoid-version +module_bluetooth_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libbluetooth-ipc.la libbluetooth-sbc.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) + +# Apple Airtunes/RAOP +module_raop_sink_la_SOURCES = modules/module-raop-sink.c +module_raop_sink_la_LDFLAGS = -module -avoid-version +module_raop_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la librtp.la libraop.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la + +module_raop_discover_la_SOURCES = modules/module-raop-discover.c +module_raop_discover_la_LDFLAGS = -module -avoid-version +module_raop_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_raop_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS) -bt_proximity_helper_SOURCES = modules/bt-proximity-helper.c -bt_proximity_helper_LDADD = $(AM_LDADD) $(BLUEZ_LIBS) -bt_proximity_helper_CFLAGS = $(AM_CFLAGS) $(BLUEZ_CFLAGS) -bt_proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)  ###################################  #        Some minor stuff         # @@ -1598,7 +1446,7 @@ daemon.conf: daemon/daemon.conf.in Makefile  install-exec-hook:  	chown root $(DESTDIR)$(bindir)/pulseaudio ; true  	chmod u+s $(DESTDIR)$(bindir)/pulseaudio -	-chmod u+s $(DESTDIR)$(pulselibexecdir)/bt-proximity-helper +	-chmod u+s $(DESTDIR)$(pulselibexecdir)/proximity-helper  	ln -sf pacat $(DESTDIR)$(bindir)/parec  	rm -f $(DESTDIR)$(modlibexecdir)/*.a  	rm -f $(DESTDIR)$(libdir)/libpulsedsp.a @@ -1615,7 +1463,7 @@ update-ffmpeg:  update-map-file:  	( echo "PULSE_0 {" ; \  	  echo "global:" ; \ -	  ctags -I PA_GCC_PURE,PA_GCC_CONST,PA_GCC_DEPRECATED,PA_GCC_PRINTF_ATTR -f - --c-kinds=p $(pulseinclude_HEADERS) | awk '/^pa_/ { print $$1 ";" }' | sort ; \ +	  ctags -I PA_GCC_MALLOC,PA_GCC_ALLOC_SIZE2,PA_GCC_ALLOC_SIZE,PA_GCC_PURE,PA_GCC_CONST,PA_GCC_DEPRECATED,PA_GCC_PRINTF_ATTR -f - --c-kinds=p $(pulseinclude_HEADERS) | awk '/^pa_/ { print $$1 ";" }' | sort ; \  	  echo "local:" ;  \  	  echo "*;" ; \  	  echo "};" ) > $(srcdir)/map-file diff --git a/src/daemon/caps.c b/src/daemon/caps.c index f7b6658b..b5cbbc63 100644 --- a/src/daemon/caps.c +++ b/src/daemon/caps.c @@ -34,6 +34,7 @@  #include <pulsecore/macro.h>  #include <pulsecore/core-error.h>  #include <pulsecore/log.h> +#include <pulsecore/core-util.h>  #ifdef HAVE_SYS_CAPABILITY_H  #include <sys/capability.h> @@ -59,7 +60,7 @@ void pa_drop_root(void) {      if (uid == 0 || geteuid() != 0)          return; -    pa_log_info(_("Dropping root priviliges.")); +    pa_log_info(_("Dropping root privileges."));  #if defined(HAVE_SETRESUID)      pa_assert_se(setresuid(uid, uid, uid) >= 0); @@ -112,9 +113,9 @@ void pa_drop_caps(void) {  #ifndef __OPTIMIZE__      /* Valgrind doesn't not know set_caps, so we bypass it here -- but -     *  only in development builts.*/ +     * only in development builds.*/ -    if (getenv("VALGRIND") && !pa_have_caps()) +    if (pa_in_valgrind() && !pa_have_caps())          return;  #endif diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c index fbd6dc32..0bfc8a92 100644 --- a/src/daemon/cmdline.c +++ b/src/daemon/cmdline.c @@ -55,6 +55,9 @@ enum {      ARG_MODULE_IDLE_TIME,      ARG_SCACHE_IDLE_TIME,      ARG_LOG_TARGET, +    ARG_LOG_META, +    ARG_LOG_TIME, +    ARG_LOG_BACKTRACE,      ARG_LOAD,      ARG_FILE,      ARG_DL_SEARCH_PATH, @@ -88,6 +91,9 @@ static const struct option long_options[] = {      {"module-idle-time",            2, 0, ARG_MODULE_IDLE_TIME},      {"scache-idle-time",            2, 0, ARG_SCACHE_IDLE_TIME},      {"log-target",                  1, 0, ARG_LOG_TARGET}, +    {"log-meta",                    2, 0, ARG_LOG_META}, +    {"log-time",                    2, 0, ARG_LOG_TIME}, +    {"log-backtrace",               1, 0, ARG_LOG_BACKTRACE},      {"load",                        1, 0, ARG_LOAD},      {"file",                        1, 0, ARG_FILE},      {"dl-search-path",              1, 0, ARG_DL_SEARCH_PATH}, @@ -148,6 +154,9 @@ void pa_cmdline_help(const char *argv0) {             "      --log-level[=LEVEL]               Increase or set verbosity level\n"             "  -v                                    Increase the verbosity level\n"             "      --log-target={auto,syslog,stderr} Specify the log target\n" +           "      --log-meta[=BOOL]                 Include code location in log messages\n" +           "      --log-time[=BOOL]                 Include timestamps in log messages\n" +           "      --log-backtrace=FRAMES            Include a backtrace in log messages\n"             "  -p, --dl-search-path=PATH             Set the search path for dynamic shared\n"             "                                        objects (plugins)\n"             "      --resample-method=METHOD          Use the specified resampling method\n" @@ -321,6 +330,24 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d                  }                  break; +            case ARG_LOG_TIME: +                if ((conf->log_time = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) { +                    pa_log(_("--log-time boolean argument")); +                    goto fail; +                } +                break; + +            case ARG_LOG_META: +                if ((conf->log_meta = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) { +                    pa_log(_("--log-meta boolean argument")); +                    goto fail; +                } +                break; + +            case ARG_LOG_BACKTRACE: +                conf->log_backtrace = (unsigned) atoi(optarg); +                break; +              case ARG_EXIT_IDLE_TIME:                  conf->exit_idle_time = atoi(optarg);                  break; diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c index 42a71f7e..a909600e 100644 --- a/src/daemon/cpulimit.c +++ b/src/daemon/cpulimit.c @@ -24,11 +24,13 @@  #endif  #include <pulse/error.h> +#include <pulse/timeval.h>  #include <pulsecore/core-util.h>  #include <pulsecore/core-error.h>  #include <pulsecore/log.h>  #include <pulsecore/macro.h> +#include <pulsecore/rtclock.h>  #include "cpulimit.h" @@ -67,7 +69,7 @@  #define CPUTIME_INTERVAL_HARD (5)  /* Time of the last CPU load check */ -static time_t last_time = 0; +static pa_usec_t last_time = 0;  /* Pipe for communicating with the main loop */  static int the_pipe[2] = {-1, -1}; @@ -100,7 +102,7 @@ static void reset_cpu_time(int t) {      n = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec + t;      pa_assert_se(getrlimit(RLIMIT_CPU, &rl) >= 0); -    rl.rlim_cur = n; +    rl.rlim_cur = (rlim_t) n;      pa_assert_se(setrlimit(RLIMIT_CPU, &rl) >= 0);  } @@ -117,20 +119,21 @@ static void signal_handler(int sig) {      pa_assert(sig == SIGXCPU);      if (phase == PHASE_IDLE) { -        time_t now; +        pa_usec_t now, elapsed;  #ifdef PRINT_CPU_LOAD          char t[256];  #endif -        time(&now); +        now = pa_rtclock_usec(); +        elapsed = now - last_time;  #ifdef PRINT_CPU_LOAD -        pa_snprintf(t, sizeof(t), "Using %0.1f%% CPU\n", (double)CPUTIME_INTERVAL_SOFT/(now-last_time)*100); +        pa_snprintf(t, sizeof(t), "Using %0.1f%% CPU\n", ((double) CPUTIME_INTERVAL_SOFT * (double) PA_USEC_PER_SEC) / (double) elapsed * 100.0);          write_err(t);  #endif -        if (CPUTIME_INTERVAL_SOFT >= ((now-last_time)*(double)CPUTIME_PERCENT/100)) { +        if (((double) CPUTIME_INTERVAL_SOFT * (double) PA_USEC_PER_SEC) >= ((double) elapsed * (double) CPUTIME_PERCENT / 100.0)) {              static const char c = 'X';              write_err("Soft CPU time limit exhausted, terminating.\n"); @@ -164,6 +167,8 @@ static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags      pa_assert(e == io_event);      pa_assert(fd == the_pipe[0]); +    pa_log("Recevied request to terminate due to CPU overload."); +      pa_read(the_pipe[0], &c, sizeof(c), NULL);      m->quit(m, 1); /* Quit the main loop */  } @@ -179,7 +184,7 @@ int pa_cpu_limit_init(pa_mainloop_api *m) {      pa_assert(the_pipe[1] == -1);      pa_assert(!installed); -    time(&last_time); +    last_time = pa_rtclock_usec();      /* Prepare the main loop pipe */      if (pipe(the_pipe) < 0) { @@ -235,7 +240,7 @@ void pa_cpu_limit_done(void) {  #else /* HAVE_SIGXCPU */ -int pa_cpu_limit_init(PA_GCC_UNUSED pa_mainloop_api *m) { +int pa_cpu_limit_init(pa_mainloop_api *m) {      return 0;  } diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c index 05c86c87..d7ffc105 100644 --- a/src/daemon/daemon-conf.c +++ b/src/daemon/daemon-conf.c @@ -74,8 +74,12 @@ static const pa_daemon_conf default_conf = {      .default_script_file = NULL,      .log_target = PA_LOG_SYSLOG,      .log_level = PA_LOG_NOTICE, +    .log_backtrace = 0, +    .log_meta = FALSE, +    .log_time = FALSE,      .resample_method = PA_RESAMPLER_AUTO,      .disable_remixing = FALSE, +    .disable_lfe_remixing = TRUE,      .config_file = NULL,      .use_pid_file = TRUE,      .system_instance = FALSE, @@ -83,7 +87,8 @@ static const pa_daemon_conf default_conf = {      .disable_shm = FALSE,      .default_n_fragments = 4,      .default_fragment_size_msec = 25, -    .default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 } +    .default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 }, +    .shm_size = 0  #ifdef HAVE_SYS_RESOURCE_H     ,.rlimit_fsize = { .value = 0, .is_set = FALSE },      .rlimit_data = { .value = 0, .is_set = FALSE }, @@ -191,7 +196,7 @@ int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) {      return 0;  } -static int parse_log_target(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +static int parse_log_target(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {      pa_daemon_conf *c = data;      pa_assert(filename); @@ -207,7 +212,7 @@ static int parse_log_target(const char *filename, unsigned line, const char *lva      return 0;  } -static int parse_log_level(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +static int parse_log_level(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {      pa_daemon_conf *c = data;      pa_assert(filename); @@ -223,7 +228,7 @@ static int parse_log_level(const char *filename, unsigned line, const char *lval      return 0;  } -static int parse_resample_method(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +static int parse_resample_method(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {      pa_daemon_conf *c = data;      pa_assert(filename); @@ -239,7 +244,7 @@ static int parse_resample_method(const char *filename, unsigned line, const char      return 0;  } -static int parse_rlimit(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +static int parse_rlimit(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {  #ifdef HAVE_SYS_RESOURCE_H      struct pa_rlimit *r = data; @@ -268,7 +273,7 @@ static int parse_rlimit(const char *filename, unsigned line, const char *lvalue,      return 0;  } -static int parse_sample_format(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +static int parse_sample_format(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {      pa_daemon_conf *c = data;      pa_sample_format_t f; @@ -286,16 +291,16 @@ static int parse_sample_format(const char *filename, unsigned line, const char *      return 0;  } -static int parse_sample_rate(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +static int parse_sample_rate(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {      pa_daemon_conf *c = data; -    int32_t r; +    uint32_t r;      pa_assert(filename);      pa_assert(lvalue);      pa_assert(rvalue);      pa_assert(data); -    if (pa_atoi(rvalue, &r) < 0 || r > (int32_t) PA_RATE_MAX || r <= 0) { +    if (pa_atou(rvalue, &r) < 0 || r > (uint32_t) PA_RATE_MAX || r <= 0) {          pa_log(_("[%s:%u] Invalid sample rate '%s'."), filename, line, rvalue);          return -1;      } @@ -304,7 +309,7 @@ static int parse_sample_rate(const char *filename, unsigned line, const char *lv      return 0;  } -static int parse_sample_channels(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +static int parse_sample_channels(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {      pa_daemon_conf *c = data;      int32_t n; @@ -322,7 +327,7 @@ static int parse_sample_channels(const char *filename, unsigned line, const char      return 0;  } -static int parse_fragments(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +static int parse_fragments(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {      pa_daemon_conf *c = data;      int32_t n; @@ -340,7 +345,7 @@ static int parse_fragments(const char *filename, unsigned line, const char *lval      return 0;  } -static int parse_fragment_size_msec(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +static int parse_fragment_size_msec(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {      pa_daemon_conf *c = data;      int32_t n; @@ -358,7 +363,7 @@ static int parse_fragment_size_msec(const char *filename, unsigned line, const c      return 0;  } -static int parse_nice_level(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +static int parse_nice_level(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {      pa_daemon_conf *c = data;      int32_t level; @@ -376,7 +381,7 @@ static int parse_nice_level(const char *filename, unsigned line, const char *lva      return 0;  } -static int parse_rtprio(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +static int parse_rtprio(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {      pa_daemon_conf *c = data;      int32_t rtprio; @@ -397,6 +402,7 @@ static int parse_rtprio(const char *filename, unsigned line, const char *lvalue,  int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {      int r = -1;      FILE *f = NULL; +    unsigned i = 0;      pa_config_item table[] = {          { "daemonize",                  pa_config_parse_bool,     NULL }, @@ -426,7 +432,12 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {          { "default-fragment-size-msec", parse_fragment_size_msec, NULL },          { "nice-level",                 parse_nice_level,         NULL },          { "disable-remixing",           pa_config_parse_bool,     NULL }, +        { "disable-lfe-remixing",       pa_config_parse_bool,     NULL },          { "load-default-script-file",   pa_config_parse_bool,     NULL }, +        { "shm-size-bytes",             pa_config_parse_size,     NULL }, +        { "log-meta",                   pa_config_parse_bool,     NULL }, +        { "log-time",                   pa_config_parse_bool,     NULL }, +        { "log-backtrace",              pa_config_parse_unsigned, NULL },  #ifdef HAVE_SYS_RESOURCE_H          { "rlimit-fsize",               parse_rlimit,             NULL },          { "rlimit-data",                parse_rlimit,             NULL }, @@ -463,96 +474,75 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {          { NULL,                         NULL,                     NULL },      }; -    table[0].data = &c->daemonize; -    table[1].data = &c->fail; -    table[2].data = &c->high_priority; -    table[3].data = &c->realtime_scheduling; -    table[4].data = &c->disallow_module_loading; -    table[5].data = &c->disallow_exit; -    table[6].data = &c->use_pid_file; -    table[7].data = &c->system_instance; -    table[8].data = &c->no_cpu_limit; -    table[9].data = &c->disable_shm; -    table[10].data = &c->exit_idle_time; -    table[11].data = &c->module_idle_time; -    table[12].data = &c->scache_idle_time; -    table[13].data = c; -    table[14].data = &c->dl_search_path; -    table[15].data = &c->default_script_file; -    table[16].data = c; -    table[17].data = c; -    table[18].data = c; -    table[19].data = c; -    table[20].data = c; -    table[21].data = c; -    table[22].data = c; -    table[23].data = c; -    table[24].data = c; -    table[25].data = c; -    table[26].data = &c->disable_remixing; -    table[27].data = &c->load_default_script_file; +    table[i++].data = &c->daemonize; +    table[i++].data = &c->fail; +    table[i++].data = &c->high_priority; +    table[i++].data = &c->realtime_scheduling; +    table[i++].data = &c->disallow_module_loading; +    table[i++].data = &c->disallow_exit; +    table[i++].data = &c->use_pid_file; +    table[i++].data = &c->system_instance; +    table[i++].data = &c->no_cpu_limit; +    table[i++].data = &c->disable_shm; +    table[i++].data = &c->exit_idle_time; +    table[i++].data = &c->module_idle_time; +    table[i++].data = &c->scache_idle_time; +    table[i++].data = c; +    table[i++].data = &c->dl_search_path; +    table[i++].data = &c->default_script_file; +    table[i++].data = c; +    table[i++].data = c; +    table[i++].data = c; +    table[i++].data = c; +    table[i++].data = c; +    table[i++].data = c; +    table[i++].data = c; +    table[i++].data = c; +    table[i++].data = c; +    table[i++].data = c; +    table[i++].data = &c->disable_remixing; +    table[i++].data = &c->disable_lfe_remixing; +    table[i++].data = &c->load_default_script_file; +    table[i++].data = &c->shm_size; +    table[i++].data = &c->log_meta; +    table[i++].data = &c->log_time; +    table[i++].data = &c->log_backtrace;  #ifdef HAVE_SYS_RESOURCE_H -    table[28].data = &c->rlimit_fsize; -    table[29].data = &c->rlimit_data; -    table[30].data = &c->rlimit_stack; -    table[31].data = &c->rlimit_as; -    table[32].data = &c->rlimit_core; -    table[33].data = &c->rlimit_nofile; -    table[34].data = &c->rlimit_as; +    table[i++].data = &c->rlimit_fsize; +    table[i++].data = &c->rlimit_data; +    table[i++].data = &c->rlimit_stack; +    table[i++].data = &c->rlimit_as; +    table[i++].data = &c->rlimit_core; +    table[i++].data = &c->rlimit_nofile; +    table[i++].data = &c->rlimit_as;  #ifdef RLIMIT_NPROC -    table[35].data = &c->rlimit_nproc; +    table[i++].data = &c->rlimit_nproc;  #endif -  #ifdef RLIMIT_MEMLOCK -#ifndef RLIMIT_NPROC -#error "Houston, we have a numbering problem!" -#endif -    table[36].data = &c->rlimit_memlock; +    table[i++].data = &c->rlimit_memlock;  #endif -  #ifdef RLIMIT_LOCKS -#ifndef RLIMIT_MEMLOCK -#error "Houston, we have a numbering problem!" -#endif -    table[37].data = &c->rlimit_locks; +    table[i++].data = &c->rlimit_locks;  #endif -  #ifdef RLIMIT_SIGPENDING -#ifndef RLIMIT_LOCKS -#error "Houston, we have a numbering problem!" -#endif -    table[38].data = &c->rlimit_sigpending; +    table[i++].data = &c->rlimit_sigpending;  #endif -  #ifdef RLIMIT_MSGQUEUE -#ifndef RLIMIT_SIGPENDING -#error "Houston, we have a numbering problem!" -#endif -    table[39].data = &c->rlimit_msgqueue; +    table[i++].data = &c->rlimit_msgqueue;  #endif -  #ifdef RLIMIT_NICE -#ifndef RLIMIT_MSGQUEUE -#error "Houston, we have a numbering problem!" -#endif -    table[40].data = &c->rlimit_nice; +    table[i++].data = &c->rlimit_nice;  #endif -  #ifdef RLIMIT_RTPRIO -#ifndef RLIMIT_NICE -#error "Houston, we have a numbering problem!" -#endif -    table[41].data = &c->rlimit_rtprio; +    table[i++].data = &c->rlimit_rtprio;  #endif -  #ifdef RLIMIT_RTTIME -#ifndef RLIMIT_RTTIME -#error "Houston, we have a numbering problem!" -#endif -    table[42].data = &c->rlimit_rttime; +    table[i++].data = &c->rlimit_rttime;  #endif  #endif +    pa_assert(i == PA_ELEMENTSOF(table)-1); +      pa_xfree(c->config_file);      c->config_file = NULL; @@ -661,11 +651,16 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {      pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);      pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));      pa_strbuf_printf(s, "disable-remixing = %s\n", pa_yes_no(c->disable_remixing)); +    pa_strbuf_printf(s, "disable-lfe-remixing = %s\n", pa_yes_no(c->disable_lfe_remixing));      pa_strbuf_printf(s, "default-sample-format = %s\n", pa_sample_format_to_string(c->default_sample_spec.format));      pa_strbuf_printf(s, "default-sample-rate = %u\n", c->default_sample_spec.rate);      pa_strbuf_printf(s, "default-sample-channels = %u\n", c->default_sample_spec.channels);      pa_strbuf_printf(s, "default-fragments = %u\n", c->default_n_fragments);      pa_strbuf_printf(s, "default-fragment-size-msec = %u\n", c->default_fragment_size_msec); +    pa_strbuf_printf(s, "shm-size-bytes = %lu\n", (unsigned long) c->shm_size); +    pa_strbuf_printf(s, "log-meta = %s\n", pa_yes_no(c->log_meta)); +    pa_strbuf_printf(s, "log-time = %s\n", pa_yes_no(c->log_time)); +    pa_strbuf_printf(s, "log-backtrace = %u\n", c->log_backtrace);  #ifdef HAVE_SYS_RESOURCE_H      pa_strbuf_printf(s, "rlimit-fsize = %li\n", c->rlimit_fsize.is_set ? (long int) c->rlimit_fsize.value : -1);      pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1); diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h index c42984f9..04a4ebe7 100644 --- a/src/daemon/daemon-conf.h +++ b/src/daemon/daemon-conf.h @@ -66,8 +66,11 @@ typedef struct pa_daemon_conf {          no_cpu_limit,          disable_shm,          disable_remixing, +        disable_lfe_remixing,          load_default_script_file, -        disallow_exit; +        disallow_exit, +        log_meta, +        log_time;      int exit_idle_time,          module_idle_time,          scache_idle_time, @@ -78,6 +81,7 @@ typedef struct pa_daemon_conf {      char *script_commands, *dl_search_path, *default_script_file;      pa_log_target_t log_target;      pa_log_level_t log_level; +    unsigned log_backtrace;      char *config_file;  #ifdef HAVE_SYS_RESOURCE_H @@ -110,6 +114,7 @@ typedef struct pa_daemon_conf {      unsigned default_n_fragments, default_fragment_size_msec;      pa_sample_spec default_sample_spec; +    size_t shm_size;  } pa_daemon_conf;  /* Allocate a new structure and fill it with sane defaults */ diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in index 33b1d61d..00a95932 100644 --- a/src/daemon/daemon.conf.in +++ b/src/daemon/daemon.conf.in @@ -26,6 +26,7 @@  ; use-pid-file = yes  ; system-instance = no  ; disable-shm = no +; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB  ; high-priority = yes  ; nice-level = -11 @@ -39,14 +40,18 @@  ; dl-search-path = (depends on architecture) -; load-defaul-script-file = yes +; load-default-script-file = yes  ; default-script-file = @PA_DEFAULT_CONFIG_FILE@  ; log-target = auto  ; log-level = notice +; log-meta = no +; log-time = no +; log-backtrace = 0  ; resample-method = speex-float-3  ; disable-remixing = no +; disable-lfe-remixing = yes  ; no-cpu-limit = no diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in index cdaa8bbd..a8318766 100755 --- a/src/daemon/default.pa.in +++ b/src/daemon/default.pa.in @@ -23,7 +23,7 @@  ### Load something into the sample cache  #load-sample-lazy x11-bell /usr/share/sounds/gtk-events/activate.wav -load-sample-lazy pulse-hotplug /usr/share/sounds/startup3.wav +#load-sample-lazy pulse-hotplug /usr/share/sounds/startup3.wav  #load-sample-lazy pulse-coldplug /usr/share/sounds/startup3.wav  #load-sample-lazy pulse-access /usr/share/sounds/generic.wav @@ -48,6 +48,11 @@ load-module module-hal-detect  load-module module-detect  .endif +### Automatically load driver modules for Bluetooth hardware +#.ifexists module-bluetooth-discover@PA_SOEXT@ +#load-module module-bluetooth-discover +#.endif +  ### Load several protocols  .ifexists module-esound-protocol-unix@PA_SOEXT@  load-module module-esound-protocol-unix @@ -67,9 +72,12 @@ load-module module-native-protocol-unix  #load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 description="RTP Multicast Sink"  #load-module module-rtp-send source=rtp.monitor +### Enable flat volumes where possible +load-module module-flat-volume +  ### Automatically restore the volume of streams and devices -load-module module-stream-restore  load-module module-device-restore +load-module module-stream-restore  ### Automatically restore the default sink/source when changed by the user during runtime  load-module module-default-device-restore diff --git a/src/daemon/dumpmodules.c b/src/daemon/dumpmodules.c index 26fb8eef..9c9f1c81 100644 --- a/src/daemon/dumpmodules.c +++ b/src/daemon/dumpmodules.c @@ -40,7 +40,7 @@  #define PREFIX "module-" -static void short_info(const char *name, PA_GCC_UNUSED const char *path, pa_modinfo *i) { +static void short_info(const char *name, const char *path, pa_modinfo *i) {      pa_assert(name);      pa_assert(i); diff --git a/src/daemon/ltdl-bind-now.c b/src/daemon/ltdl-bind-now.c index 6821aac4..2d80fc7a 100644 --- a/src/daemon/ltdl-bind-now.c +++ b/src/daemon/ltdl-bind-now.c @@ -24,11 +24,11 @@  #include <config.h>  #endif -#if HAVE_DLFCN_H +#ifdef HAVE_DLFCN_H  #include <dlfcn.h>  #endif -#if HAVE_SYS_DL_H +#ifdef HAVE_SYS_DL_H  #include <sys/dl.h>  #endif @@ -74,7 +74,9 @@ static lt_module bind_now_open(lt_user_data d, const char *fname, lt_dladvise ad      pa_assert(fname);      if (!(m = dlopen(fname, PA_BIND_NOW))) { +#ifdef HAVE_LT_DLMUTEX_REGISTER          libtool_set_error(dlerror()); +#endif          return NULL;      } @@ -86,7 +88,9 @@ static int bind_now_close(lt_user_data d, lt_module m) {      pa_assert(m);      if (dlclose(m) != 0){ +#ifdef HAVE_LT_DLMUTEX_REGISTER          libtool_set_error(dlerror()); +#endif          return 1;      } @@ -100,7 +104,9 @@ static lt_ptr bind_now_find_sym(lt_user_data d, lt_module m, const char *symbol)      pa_assert(symbol);      if (!(ptr = dlsym(m, symbol))) { +#ifdef HAVE_LT_DLMUTEX_REGISTER          libtool_set_error(dlerror()); +#endif          return NULL;      } diff --git a/src/daemon/main.c b/src/daemon/main.c index ab438320..f6d2512c 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -66,6 +66,7 @@  #include <pulse/xmalloc.h>  #include <pulse/i18n.h> +#include <pulsecore/lock-autospawn.h>  #include <pulsecore/winsock.h>  #include <pulsecore/core-error.h>  #include <pulsecore/core.h> @@ -95,8 +96,6 @@  #include "ltdl-bind-now.h"  #include "polkit.h" -#define AUTOSPAWN_LOCK "autospawn.lock" -  #ifdef HAVE_LIBWRAP  /* Only one instance of these variables */  int allow_severity = LOG_INFO; @@ -112,7 +111,7 @@ int __padsp_disabled__ = 7;  #ifdef OS_IS_WIN32 -static void message_cb(pa_mainloop_api*a, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { +static void message_cb(pa_mainloop_api*a, pa_time_event*e, const struct timeval *tv, void *userdata) {      MSG msg;      struct timeval tvnext; @@ -131,7 +130,7 @@ static void message_cb(pa_mainloop_api*a, pa_time_event*e, PA_GCC_UNUSED const s  #endif -static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e, int sig, void *userdata) { +static void signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {      pa_log_info(_("Got signal %s."), pa_sig2str(sig));      switch (sig) { @@ -223,7 +222,7 @@ static int change_user(void) {  #elif defined(HAVE_SETREGID)      r = setregid(gr->gr_gid, gr->gr_gid);  #else -#error "No API to drop priviliges" +#error "No API to drop privileges"  #endif      if (r < 0) { @@ -239,7 +238,7 @@ static int change_user(void) {  #elif defined(HAVE_SETREUID)      r = setreuid(pw->pw_uid, pw->pw_uid);  #else -#error "No API to drop priviliges" +#error "No API to drop privileges"  #endif      if (r < 0) { @@ -346,7 +345,11 @@ int main(int argc, char *argv[]) {      struct timeval win32_tv;  #endif      char *lf = NULL; -    int autospawn_lock_fd = -1; +    int autospawn_fd = -1; +    pa_bool_t autospawn_locked = FALSE; + +    pa_log_set_maximal_level(PA_LOG_INFO); +    pa_log_set_ident("pulseaudio");  #if defined(__linux__) && defined(__OPTIMIZE__)      /* @@ -379,7 +382,7 @@ int main(int argc, char *argv[]) {          /* Drop all capabilities except CAP_SYS_NICE  */          pa_limit_caps(); -        /* Drop priviliges, but keep CAP_SYS_NICE */ +        /* Drop privileges, but keep CAP_SYS_NICE */          pa_drop_root();          /* After dropping root, the effective set is reset, hence, @@ -410,9 +413,6 @@ int main(int argc, char *argv[]) {      setlocale(LC_ALL, "");      pa_init_i18n(); -    pa_log_set_maximal_level(PA_LOG_INFO); -    pa_log_set_ident("pulseaudio"); -      conf = pa_daemon_conf_new();      if (pa_daemon_conf_load(conf, NULL) < 0) @@ -428,6 +428,9 @@ int main(int argc, char *argv[]) {      pa_log_set_maximal_level(conf->log_level);      pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL); +    pa_log_set_show_meta(conf->log_meta); +    pa_log_set_show_backtrace(conf->log_backtrace); +    pa_log_set_show_time(conf->log_time);      pa_log_debug("Started as real root: %s, suid root: %s", pa_yes_no(real_root), pa_yes_no(suid_root)); @@ -476,9 +479,9 @@ int main(int argc, char *argv[]) {              pa_drop_caps();              if (conf->high_priority || conf->realtime_scheduling) -                pa_log_notice(_("Called SUID root and real-time/high-priority scheduling was requested in the configuration. However, we lack the necessary priviliges:\n" -                                "We are not in group '"PA_REALTIME_GROUP"' and PolicyKit refuse to grant us priviliges. Dropping SUID again.\n" -                                "For enabling real-time scheduling please acquire the appropriate PolicyKit priviliges, or become a member of '"PA_REALTIME_GROUP"', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user.")); +                pa_log_notice(_("Called SUID root and real-time/high-priority scheduling was requested in the configuration. However, we lack the necessary privileges:\n" +                                "We are not in group '"PA_REALTIME_GROUP"' and PolicyKit refuse to grant us privileges. Dropping SUID again.\n" +                                "For enabling real-time scheduling please acquire the appropriate PolicyKit privileges, or become a member of '"PA_REALTIME_GROUP"', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user."));          }      } @@ -606,7 +609,7 @@ int main(int argc, char *argv[]) {          case PA_CMD_KILL:              if (pa_pid_file_kill(SIGINT, NULL, "pulseaudio") < 0) -                pa_log(_("Failed to kill daemon.")); +                pa_log(_("Failed to kill daemon: %s"), pa_cstrerror(errno));              else                  retval = 0; @@ -626,7 +629,7 @@ int main(int argc, char *argv[]) {      if (real_root && !conf->system_instance)          pa_log_warn(_("This program is not intended to be run as root (unless --system is specified)."));      else if (!real_root && conf->system_instance) { -        pa_log(_("Root priviliges required.")); +        pa_log(_("Root privileges required."));          goto finish;      } @@ -646,9 +649,9 @@ int main(int argc, char *argv[]) {          conf->disable_shm = TRUE;      } -    if (conf->system_instance && conf->exit_idle_time > 0) { +    if (conf->system_instance && conf->exit_idle_time >= 0) {          pa_log_notice(_("Running in system mode, forcibly disabling exit idle time!")); -        conf->exit_idle_time = 0; +        conf->exit_idle_time = -1;      }      if (conf->cmd == PA_CMD_START) { @@ -656,8 +659,17 @@ int main(int argc, char *argv[]) {           * first take the autospawn lock to make things           * synchronous. */ -        lf = pa_runtime_path(AUTOSPAWN_LOCK); -        autospawn_lock_fd = pa_lock_lockfile(lf); +        if ((autospawn_fd = pa_autospawn_lock_init()) < 0) { +            pa_log("Failed to initialize autospawn lock"); +            goto finish; +        } + +        if ((pa_autospawn_lock_acquire(TRUE) < 0)) { +            pa_log("Failed to acquire autospawn lock"); +            goto finish; +        } + +        autospawn_locked = TRUE;      }      if (conf->daemonize) { @@ -703,12 +715,15 @@ int main(int argc, char *argv[]) {              goto finish;          } -        if (autospawn_lock_fd >= 0) { +        if (autospawn_fd >= 0) {              /* The lock file is unlocked from the parent, so we need               * to close it in the child */ -            pa_close(autospawn_lock_fd); -            autospawn_lock_fd = -1; +            pa_autospawn_lock_release(); +            pa_autospawn_lock_done(TRUE); + +            autospawn_locked = FALSE; +            autospawn_fd = -1;          }          pa_assert_se(pa_close(daemon_pipe[0]) == 0); @@ -766,8 +781,29 @@ int main(int argc, char *argv[]) {      pa_set_env("PULSE_SYSTEM", conf->system_instance ? "1" : "0");      pa_log_info(_("This is PulseAudio %s"), PACKAGE_VERSION); +    pa_log_debug(_("Compilation host: %s"), CANONICAL_HOST); +    pa_log_debug(_("Compilation CFLAGS: %s"), PA_CFLAGS); + +    s = pa_uname_string(); +    pa_log_debug(_("Running on host: %s"), s); +    pa_xfree(s); +      pa_log_info(_("Page size is %lu bytes"), (unsigned long) PA_PAGE_SIZE); +#ifdef HAVE_VALGRIND_MEMCHECK_H +    pa_log_debug(_("Compiled with Valgrind support: yes")); +#else +    pa_log_debug(_("Compiled with Valgrind support: no")); +#endif + +    pa_log_debug(_("Running in valgrind mode: %s"), pa_yes_no(pa_in_valgrind())); + +#ifdef __OPTIMIZE__ +    pa_log_debug(_("Optimized build: yes")); +#else +    pa_log_debug(_("Optimized build: no")); +#endif +      if (!(s = pa_machine_id())) {          pa_log(_("Failed to get machine ID"));          goto finish; @@ -823,7 +859,7 @@ int main(int argc, char *argv[]) {      pa_assert_se(mainloop = pa_mainloop_new()); -    if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm))) { +    if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm, conf->shm_size))) {          pa_log(_("pa_core_new() failed."));          goto finish;      } @@ -838,6 +874,7 @@ int main(int argc, char *argv[]) {      c->realtime_priority = conf->realtime_priority;      c->realtime_scheduling = !!conf->realtime_scheduling;      c->disable_remixing = !!conf->disable_remixing; +    c->disable_lfe_remixing = !!conf->disable_lfe_remixing;      c->running_as_daemon = !!conf->daemonize;      c->disallow_exit = conf->disallow_exit; @@ -917,8 +954,12 @@ int main(int argc, char *argv[]) {  finish: -    if (autospawn_lock_fd >= 0) -        pa_unlock_lockfile(lf, autospawn_lock_fd); +    if (autospawn_fd >= 0) { +        if (autospawn_locked) +            pa_autospawn_lock_release(); + +        pa_autospawn_lock_done(FALSE); +    }      if (lf)          pa_xfree(lf); diff --git a/src/daemon/system.pa.in b/src/daemon/system.pa.in index f6052c4b..27e42815 100755 --- a/src/daemon/system.pa.in +++ b/src/daemon/system.pa.in @@ -34,8 +34,9 @@ load-module module-esound-protocol-unix  .endif  load-module module-native-protocol-unix -### Automatically restore the volume of playback streams -load-module module-volume-restore +### Automatically restore the volume of streams and devices +load-module module-stream-restore +load-module module-device-restore  ### Automatically restore the default sink/source when changed by the user during runtime  load-module module-default-device-restore diff --git a/src/map-file b/src/map-file index b6d3b63d..ca523b91 100644 --- a/src/map-file +++ b/src/map-file @@ -9,6 +9,7 @@ pa_browser_unref;  pa_bytes_per_second;  pa_bytes_snprint;  pa_bytes_to_usec; +pa_channel_map_compatible;  pa_channel_map_equal;  pa_channel_map_init;  pa_channel_map_init_auto; @@ -97,7 +98,10 @@ pa_context_unload_module;  pa_context_unref;  pa_cvolume_avg;  pa_cvolume_channels_equal_to; +pa_cvolume_compatible;  pa_cvolume_equal; +pa_cvolume_init; +pa_cvolume_max;  pa_cvolume_remap;  pa_cvolume_set;  pa_cvolume_snprint; @@ -158,6 +162,7 @@ pa_proplist_update;  pa_sample_format_to_string;  pa_sample_size;  pa_sample_spec_equal; +pa_sample_spec_init;  pa_sample_spec_snprint;  pa_sample_spec_valid;  pa_signal_done; @@ -222,7 +227,10 @@ pa_stream_update_timing_info;  pa_stream_writable_size;  pa_stream_write;  pa_strerror; +pa_sw_cvolume_divide;  pa_sw_cvolume_multiply; +pa_sw_cvolume_snprint_dB; +pa_sw_volume_divide;  pa_sw_volume_from_dB;  pa_sw_volume_from_linear;  pa_sw_volume_multiply; diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index 8abf834d..20dc400f 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -30,6 +30,7 @@  #include <pulse/sample.h>  #include <pulse/xmalloc.h> +#include <pulse/timeval.h>  #include <pulsecore/log.h>  #include <pulsecore/macro.h> @@ -39,7 +40,7 @@  #include "alsa-util.h"  struct pa_alsa_fdlist { -    int num_fds; +    unsigned num_fds;      struct pollfd *fds;      /* This is a temporary buffer used to avoid lots of mallocs */      struct pollfd *work_fds; @@ -50,16 +51,17 @@ struct pa_alsa_fdlist {      pa_defer_event *defer;      pa_io_event **ios; -    int polled; +    pa_bool_t polled;      void (*cb)(void *userdata);      void *userdata;  }; -static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void *userdata) { +static void io_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {      struct pa_alsa_fdlist *fdl = userdata; -    int err, i; +    int err; +    unsigned i;      unsigned short revents;      pa_assert(a); @@ -71,11 +73,11 @@ static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io      if (fdl->polled)          return; -    fdl->polled = 1; +    fdl->polled = TRUE;      memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds); -    for (i = 0;i < fdl->num_fds; i++) { +    for (i = 0; i < fdl->num_fds; i++) {          if (e == fdl->ios[i]) {              if (events & PA_IO_EVENT_INPUT)                  fdl->work_fds[i].revents |= POLLIN; @@ -102,9 +104,10 @@ static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io          snd_mixer_handle_events(fdl->mixer);  } -static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *userdata) { +static void defer_cb(pa_mainloop_api*a, pa_defer_event* e, void *userdata) {      struct pa_alsa_fdlist *fdl = userdata; -    int num_fds, i, err; +    unsigned num_fds, i; +    int err;      struct pollfd *temp;      pa_assert(a); @@ -113,8 +116,7 @@ static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *u      a->defer_enable(fdl->defer, 0); -    num_fds = snd_mixer_poll_descriptors_count(fdl->mixer); -    pa_assert(num_fds > 0); +    num_fds = (unsigned) snd_mixer_poll_descriptors_count(fdl->mixer);      if (num_fds != fdl->num_fds) {          if (fdl->fds) @@ -132,7 +134,7 @@ static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *u          return;      } -    fdl->polled = 0; +    fdl->polled = FALSE;      if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)          return; @@ -176,7 +178,7 @@ struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {      fdl->m = NULL;      fdl->defer = NULL;      fdl->ios = NULL; -    fdl->polled = 0; +    fdl->polled = FALSE;      return fdl;  } @@ -190,9 +192,9 @@ void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {      }      if (fdl->ios) { -        int i; +        unsigned i;          pa_assert(fdl->m); -        for (i = 0;i < fdl->num_fds;i++) +        for (i = 0; i < fdl->num_fds; i++)              fdl->m->io_free(fdl->ios[i]);          pa_xfree(fdl->ios);      } @@ -369,11 +371,18 @@ int pa_alsa_set_hw_params(          goto finish;      if (_periods > 0) { -        dir = 1; + +        /* First we pass 0 as direction to get exactly what we asked +         * for. That this is necessary is presumably a bug in ALSA */ + +        dir = 0;          if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) { -            dir = -1; -            if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) -                goto finish; +            dir = 1; +            if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) { +                dir = -1; +                if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) +                    goto finish; +            }          }      } @@ -403,7 +412,7 @@ int pa_alsa_set_hw_params(      /* If the sample rate deviates too much, we need to resample */      if (r < ss->rate*.95 || r > ss->rate*1.05)          ss->rate = r; -    ss->channels = c; +    ss->channels = (uint8_t) c;      ss->format = f;      pa_assert(_periods > 0); @@ -420,6 +429,8 @@ int pa_alsa_set_hw_params(      ret = 0; +    snd_pcm_nonblock(pcm_handle, 1); +  finish:      return ret; @@ -568,40 +579,60 @@ snd_pcm_t *pa_alsa_open_by_device_id(              continue;          d = pa_sprintf_malloc("%s:%s", device_table[i].name, dev_id); -        pa_log_debug("Trying %s...", d); -        if ((err = snd_pcm_open(&pcm_handle, d, mode, -                                SND_PCM_NONBLOCK| -                                SND_PCM_NO_AUTO_RESAMPLE| -                                SND_PCM_NO_AUTO_CHANNELS| -                                SND_PCM_NO_AUTO_FORMAT | -                                SND_PCM_NO_SOFTVOL)) < 0) { -            pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err)); -            pa_xfree(d); -            continue; -        } +        for (;;) { +            pa_log_debug("Trying %s...", d); + +            /* We don't pass SND_PCM_NONBLOCK here, since alsa-lib <= +             * 1.0.17a would then ignore the SND_PCM_NO_xxx +             * flags. Instead we enable nonblock mode afterwards via +             * snd_pcm_nonblock(). Also see +             * http://mailman.alsa-project.org/pipermail/alsa-devel/2008-August/010258.html */ + +            if ((err = snd_pcm_open(&pcm_handle, d, mode, +                                    /* SND_PCM_NONBLOCK| */ +                                    SND_PCM_NO_AUTO_RESAMPLE| +                                    SND_PCM_NO_AUTO_CHANNELS| +                                    SND_PCM_NO_AUTO_FORMAT)) < 0) { +                pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err)); +                break; +            } -        try_ss.channels = device_table[i].map.channels; -        try_ss.rate = ss->rate; -        try_ss.format = ss->format; +            try_ss.channels = device_table[i].map.channels; +            try_ss.rate = ss->rate; +            try_ss.format = ss->format; -        if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) { -            pa_log_info("PCM device %s refused our hw parameters: %s", d, snd_strerror(err)); -            pa_xfree(d); -            snd_pcm_close(pcm_handle); -            continue; +            if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) { + +                if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) { +                    char *t; + +                    t = pa_sprintf_malloc("plug:%s", d); +                    pa_xfree(d); +                    d = t; + +                    snd_pcm_close(pcm_handle); +                    continue; +                } + +                pa_log_info("PCM device %s refused our hw parameters: %s", d, snd_strerror(err)); +                snd_pcm_close(pcm_handle); +                break; +            } + +            *ss = try_ss; +            *map = device_table[i].map; +            pa_assert(map->channels == ss->channels); +            *dev = d; +            return pcm_handle;          } -        *ss = try_ss; -        *map = device_table[i].map; -        pa_assert(map->channels == ss->channels); -        *dev = d; -        return pcm_handle; +        pa_xfree(d);      }      /* OK, we didn't find any good device, so let's try the raw plughw: stuff */ -    d = pa_sprintf_malloc("plughw:%s", dev_id); +    d = pa_sprintf_malloc("hw:%s", dev_id);      pa_log_debug("Trying %s as last resort...", d);      pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched);      pa_xfree(d); @@ -635,8 +666,16 @@ snd_pcm_t *pa_alsa_open_by_device_string(      d = pa_xstrdup(device);      for (;;) { +        pa_log_debug("Trying %s...", d); + +        /* We don't pass SND_PCM_NONBLOCK here, since alsa-lib <= +         * 1.0.17a would then ignore the SND_PCM_NO_xxx flags. Instead +         * we enable nonblock mode afterwards via +         * snd_pcm_nonblock(). Also see +         * http://mailman.alsa-project.org/pipermail/alsa-devel/2008-August/010258.html */ -        if ((err = snd_pcm_open(&pcm_handle, d, mode, SND_PCM_NONBLOCK| +        if ((err = snd_pcm_open(&pcm_handle, d, mode, +                                /*SND_PCM_NONBLOCK|*/                                  SND_PCM_NO_AUTO_RESAMPLE|                                  SND_PCM_NO_AUTO_CHANNELS|                                  SND_PCM_NO_AUTO_FORMAT)) < 0) { @@ -647,24 +686,23 @@ snd_pcm_t *pa_alsa_open_by_device_string(          if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE)) < 0) { -            if (err == -EPERM) { -                /* Hmm, some hw is very exotic, so we retry with plug, if without it didn't work */ +            /* Hmm, some hw is very exotic, so we retry with plug, if without it didn't work */ -                if (pa_startswith(d, "hw:")) { -                    char *t = pa_sprintf_malloc("plughw:%s", d+3); -                    pa_log_debug("Opening the device as '%s' didn't work, retrying with '%s'.", d, t); -                    pa_xfree(d); -                    d = t; +            if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) { +                char *t; -                    snd_pcm_close(pcm_handle); -                    continue; -                } - -                pa_log("Failed to set hardware parameters on %s: %s", d, snd_strerror(err)); +                t = pa_sprintf_malloc("plug:%s", d);                  pa_xfree(d); +                d = t; +                  snd_pcm_close(pcm_handle); -                return NULL; +                continue;              } + +            pa_log("Failed to set hardware parameters on %s: %s", d, snd_strerror(err)); +            pa_xfree(d); +            snd_pcm_close(pcm_handle); +            return NULL;          }          *dev = d; @@ -808,7 +846,7 @@ int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel      if (channel_map->channels > 1 &&          ((playback && snd_mixer_selem_has_playback_volume_joined(elem)) ||           (!playback && snd_mixer_selem_has_capture_volume_joined(elem)))) { -        pa_log_info("ALSA device lacks independant volume controls for each channel, falling back to software volume control."); +        pa_log_info("ALSA device lacks independant volume controls for each channel.");          return -1;      } @@ -820,7 +858,7 @@ int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel          id = alsa_channel_ids[channel_map->map[i]];          if (!is_mono && id == SND_MIXER_SCHN_UNKNOWN) { -            pa_log_info("Configured channel map contains channel '%s' that is unknown to the ALSA mixer. Falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i])); +            pa_log_info("Configured channel map contains channel '%s' that is unknown to the ALSA mixer.", pa_channel_position_to_string(channel_map->map[i]));              return -1;          } @@ -832,7 +870,7 @@ int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel          if ((playback && (!snd_mixer_selem_has_playback_channel(elem, id) || (is_mono && !snd_mixer_selem_is_playback_mono(elem)))) ||              (!playback && (!snd_mixer_selem_has_capture_channel(elem, id) || (is_mono && !snd_mixer_selem_is_capture_mono(elem))))) { -            pa_log_info("ALSA device lacks separate volumes control for channel '%s', falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i])); +            pa_log_info("ALSA device lacks separate volumes control for channel '%s'", pa_channel_position_to_string(channel_map->map[i]));              return -1;          } @@ -850,56 +888,6 @@ int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel      return 0;  } -void pa_alsa_0dB_playback(snd_mixer_elem_t *elem) { -    long min, max, v; - -    pa_assert(elem); - -    /* Try to enable 0 dB if possible. If ALSA cannot do dB, then use -     * raw volume levels and fix them to 75% */ - -    if (snd_mixer_selem_set_playback_dB_all(elem, 0, -1) >= 0) -        return; - -    if (snd_mixer_selem_set_playback_dB_all(elem, 0, 1) >= 0) -        return; - -    if (snd_mixer_selem_get_playback_volume_range(elem, &min, &max) < 0) -        return; - -    v = min + ((max - min) * 3) / 4; /* 75% */ - -    if (v <= min) -        v = max; - -    snd_mixer_selem_set_playback_volume_all(elem, v); -} - -void pa_alsa_0dB_capture(snd_mixer_elem_t *elem) { -    long min, max, v; - -    pa_assert(elem); - -    /* Try to enable 0 dB if possible. If ALSA cannot do dB, then use -     * raw volume levels and fix them to 75% */ - -    if (snd_mixer_selem_set_capture_dB_all(elem, 0, -1) >= 0) -        return; - -    if (snd_mixer_selem_set_capture_dB_all(elem, 0, 1) >= 0) -        return; - -    if (snd_mixer_selem_get_capture_volume_range(elem, &min, &max) < 0) -        return; - -    v = min + ((max - min) * 3) / 4; /* 75% */ - -    if (v <= min) -        v = max; - -    snd_mixer_selem_set_capture_volume_all(elem, v); -} -  void pa_alsa_dump(snd_pcm_t *pcm) {      int err;      snd_output_t *out; @@ -945,12 +933,17 @@ void pa_alsa_dump_status(snd_pcm_t *pcm) {  static void alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt,...) {      va_list ap; +    char *alsa_file; + +    alsa_file = pa_sprintf_malloc("(alsa-lib)%s", file);      va_start(ap, fmt); -    pa_log_levelv_meta(PA_LOG_WARN, file, line, function, fmt, ap); +    pa_log_levelv_meta(PA_LOG_INFO, alsa_file, line, function, fmt, ap);      va_end(ap); + +    pa_xfree(alsa_file);  }  static pa_atomic_t n_error_handler_installed = PA_ATOMIC_INIT(0); @@ -1058,6 +1051,12 @@ int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {          pa_log_warn("Got POLLNVAL from ALSA");      if (revents & POLLHUP)          pa_log_warn("Got POLLHUP from ALSA"); +    if (revents & POLLPRI) +        pa_log_warn("Got POLLPRI from ALSA"); +    if (revents & POLLIN) +        pa_log_warn("Got POLLIN from ALSA"); +    if (revents & POLLOUT) +        pa_log_warn("Got POLLOUT from ALSA");      state = snd_pcm_state(pcm);      pa_log_warn("PCM state is %s", snd_pcm_state_name(state)); @@ -1106,10 +1105,10 @@ pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) {          return NULL;      } -    item = pa_rtpoll_item_new(rtpoll, PA_RTPOLL_NEVER, n); +    item = pa_rtpoll_item_new(rtpoll, PA_RTPOLL_NEVER, (unsigned) n);      pollfd = pa_rtpoll_item_get_pollfd(item, NULL); -    if ((err = snd_pcm_poll_descriptors(pcm, pollfd, n)) < 0) { +    if ((err = snd_pcm_poll_descriptors(pcm, pollfd, (unsigned) n)) < 0) {          pa_log("snd_pcm_poll_descriptors() failed: %s", snd_strerror(err));          pa_rtpoll_item_free(item);          return NULL; @@ -1117,3 +1116,62 @@ 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 n; +    size_t k; + +    pa_assert(pcm); +    pa_assert(hwbuf_size > 0); +    pa_assert(ss); + +    /* Some ALSA driver expose weird bugs, let's inform the user about +     * what is going on */ + +    n = snd_pcm_avail_update(pcm); + +    if (n <= 0) +        return n; + +    k = (size_t) n * pa_frame_size(ss); + +    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.", +               (unsigned long) k, (unsigned long) pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC); + +    return n; +} + +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) { +    int r; +    snd_pcm_uframes_t before; +    size_t k; + +    pa_assert(pcm); +    pa_assert(areas); +    pa_assert(offset); +    pa_assert(frames); +    pa_assert(hwbuf_size > 0); +    pa_assert(ss); + +    before = *frames; + +    r = snd_pcm_mmap_begin(pcm, areas, offset, frames); + +    if (r < 0) +        return r; + +    k = (size_t) *frames * pa_frame_size(ss); + +    if (*frames > before || +        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.", +               (unsigned long) k, (unsigned long) pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC); + +    return r; +} diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h index 4de8bcd2..aaa01c78 100644 --- a/src/modules/alsa-util.h +++ b/src/modules/alsa-util.h @@ -26,6 +26,7 @@  #include <asoundlib.h>  #include <pulse/sample.h> +#include <pulse/volume.h>  #include <pulse/mainloop-api.h>  #include <pulse/channelmap.h>  #include <pulse/proplist.h> @@ -79,9 +80,6 @@ snd_pcm_t *pa_alsa_open_by_device_string(  int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback); -void pa_alsa_0dB_playback(snd_mixer_elem_t *elem); -void pa_alsa_0dB_capture(snd_mixer_elem_t *elem); -  void pa_alsa_dump(snd_pcm_t *pcm);  void pa_alsa_dump_status(snd_pcm_t *pcm); @@ -94,4 +92,7 @@ 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); +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); +  #endif diff --git a/src/modules/bluetooth/Makefile b/src/modules/bluetooth/Makefile new file mode 120000 index 00000000..efe5a336 --- /dev/null +++ b/src/modules/bluetooth/Makefile @@ -0,0 +1 @@ +../../pulse/Makefile
\ No newline at end of file diff --git a/src/modules/bluetooth/ipc.c b/src/modules/bluetooth/ipc.c new file mode 100644 index 00000000..98256998 --- /dev/null +++ b/src/modules/bluetooth/ipc.c @@ -0,0 +1,118 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org> + * + *  This library is free software; you can redistribute it and/or + *  modify it under the terms of the GNU Lesser General Public + *  License as published by the Free Software Foundation; either + *  version 2.1 of the License, or (at your option) any later version. + * + *  This library is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + *  Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this library; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#include "ipc.h" + +/* This table contains the string representation for messages */ +static const char *strmsg[] = { +	"BT_GETCAPABILITIES_REQ", +	"BT_GETCAPABILITIES_RSP", +	"BT_SETCONFIGURATION_REQ", +	"BT_SETCONFIGURATION_RSP", +	"BT_STREAMSTART_REQ", +	"BT_STREAMSTART_RSP", +	"BT_STREAMSTOP_REQ", +	"BT_STREAMSTOP_RSP", +	"BT_STREAMSUSPEND_IND", +	"BT_STREAMRESUME_IND", +	"BT_CONTROL_REQ", +	"BT_CONTROL_RSP", +	"BT_CONTROL_IND", +	"BT_STREAMFD_IND", +}; + +int bt_audio_service_open(void) +{ +	int sk; +	int err; +	struct sockaddr_un addr = { +		AF_UNIX, BT_IPC_SOCKET_NAME +	}; + +	sk = socket(PF_LOCAL, SOCK_STREAM, 0); +	if (sk < 0) { +		err = errno; +		fprintf(stderr, "%s: Cannot open socket: %s (%d)\n", +			__FUNCTION__, strerror(err), err); +		errno = err; +		return -1; +	} + +	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { +		err = errno; +		fprintf(stderr, "%s: connect() failed: %s (%d)\n", +			__FUNCTION__, strerror(err), err); +		close(sk); +		errno = err; +		return -1; +	} + +	return sk; +} + +int bt_audio_service_close(int sk) +{ +	return close(sk); +} + +int bt_audio_service_get_data_fd(int sk) +{ +	char cmsg_b[CMSG_SPACE(sizeof(int))], m; +	int err, ret; +	struct iovec iov = { &m, sizeof(m) }; +	struct msghdr msgh; +	struct cmsghdr *cmsg; + +	memset(&msgh, 0, sizeof(msgh)); +	msgh.msg_iov = &iov; +	msgh.msg_iovlen = 1; +	msgh.msg_control = &cmsg_b; +	msgh.msg_controllen = CMSG_LEN(sizeof(int)); + +	ret = (int) recvmsg(sk, &msgh, 0); +	if (ret < 0) { +		err = errno; +		fprintf(stderr, "%s: Unable to receive fd: %s (%d)\n", +			__FUNCTION__, strerror(err), err); +		errno = err; +		return -1; +	} + +	/* Receive auxiliary data in msgh */ +	for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; +			cmsg = CMSG_NXTHDR(&msgh, cmsg)) { +		if (cmsg->cmsg_level == SOL_SOCKET +				&& cmsg->cmsg_type == SCM_RIGHTS) +			return (*(int *) CMSG_DATA(cmsg)); +	} + +	errno = EINVAL; +	return -1; +} + +const char *bt_audio_strmsg(int type) +{ +    if (type < 0 || (size_t) type > (sizeof(strmsg) / sizeof(strmsg[0]))) +		return NULL; + +	return strmsg[type]; +} diff --git a/src/modules/bluetooth/ipc.h b/src/modules/bluetooth/ipc.h new file mode 100644 index 00000000..ae85e727 --- /dev/null +++ b/src/modules/bluetooth/ipc.h @@ -0,0 +1,308 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org> + * + *  This library is free software; you can redistribute it and/or + *  modify it under the terms of the GNU Lesser General Public + *  License as published by the Free Software Foundation; either + *  version 2.1 of the License, or (at your option) any later version. + * + *  This library is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + *  Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this library; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +/* +  Message sequence chart of streaming sequence for A2DP transport + +  Audio daemon                       User +                             on snd_pcm_open +                 <--BT_GETCAPABILITIES_REQ + +  BT_GETCAPABILITIES_RSP--> + +                        on snd_pcm_hw_params +                <--BT_SETCONFIGURATION_REQ + +  BT_SETCONFIGURATION_RSP--> + +			on snd_pcm_prepare +                <--BT_STREAMSTART_REQ + +  <Moves to streaming state> +  BT_STREAMSTART_RSP--> + +  BT_STREAMFD_IND --> + +                          <  streams data > +                             .......... + +               on snd_pcm_drop/snd_pcm_drain + +                <--BT_STREAMSTOP_REQ + +  <Moves to open state> +  BT_STREAMSTOP_RSP--> + +			on IPC close or appl crash +  <Moves to idle> + + */ + +#ifndef BT_AUDIOCLIENT_H +#define BT_AUDIOCLIENT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> + +#define BT_AUDIO_IPC_PACKET_SIZE   128 +#define BT_IPC_SOCKET_NAME "\0/org/bluez/audio" + +/* Generic message header definition, except for RSP messages */ +typedef struct { +	uint8_t msg_type; +} __attribute__ ((packed)) bt_audio_msg_header_t; + +/* Generic message header definition, for all RSP messages */ +typedef struct { +	bt_audio_msg_header_t	msg_h; +	uint8_t			posix_errno; +} __attribute__ ((packed)) bt_audio_rsp_msg_header_t; + +/* Messages list */ +#define BT_GETCAPABILITIES_REQ		0 +#define BT_GETCAPABILITIES_RSP		1 + +#define BT_SETCONFIGURATION_REQ		2 +#define BT_SETCONFIGURATION_RSP		3 + +#define BT_STREAMSTART_REQ		4 +#define BT_STREAMSTART_RSP		5 + +#define BT_STREAMSTOP_REQ		6 +#define BT_STREAMSTOP_RSP		7 + +#define BT_STREAMSUSPEND_IND		8 +#define BT_STREAMRESUME_IND		9 + +#define BT_CONTROL_REQ		       10 +#define BT_CONTROL_RSP		       11 +#define BT_CONTROL_IND		       12 + +#define BT_STREAMFD_IND		       13 + +/* BT_GETCAPABILITIES_REQ */ + +#define BT_CAPABILITIES_TRANSPORT_A2DP	0 +#define BT_CAPABILITIES_TRANSPORT_SCO	1 +#define BT_CAPABILITIES_TRANSPORT_ANY	2 + +#define BT_CAPABILITIES_ACCESS_MODE_READ	1 +#define BT_CAPABILITIES_ACCESS_MODE_WRITE	2 +#define BT_CAPABILITIES_ACCESS_MODE_READWRITE	3 + +#define BT_FLAG_AUTOCONNECT	1 + +struct bt_getcapabilities_req { +	bt_audio_msg_header_t	h; +	char			device[18];	/* Address of the remote Device */ +	uint8_t			transport;	/* Requested transport */ +	uint8_t			flags;		/* Requested flags */ +} __attribute__ ((packed)); + +/* BT_GETCAPABILITIES_RSP */ + +/** + * SBC Codec parameters as per A2DP profile 1.0 § 4.3 + */ + +#define BT_SBC_SAMPLING_FREQ_16000		(1 << 3) +#define BT_SBC_SAMPLING_FREQ_32000		(1 << 2) +#define BT_SBC_SAMPLING_FREQ_44100		(1 << 1) +#define BT_SBC_SAMPLING_FREQ_48000		1 + +#define BT_A2DP_CHANNEL_MODE_MONO		(1 << 3) +#define BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL	(1 << 2) +#define BT_A2DP_CHANNEL_MODE_STEREO		(1 << 1) +#define BT_A2DP_CHANNEL_MODE_JOINT_STEREO	1 + +#define BT_A2DP_BLOCK_LENGTH_4			(1 << 3) +#define BT_A2DP_BLOCK_LENGTH_8			(1 << 2) +#define BT_A2DP_BLOCK_LENGTH_12			(1 << 1) +#define BT_A2DP_BLOCK_LENGTH_16			1 + +#define BT_A2DP_SUBBANDS_4			(1 << 1) +#define BT_A2DP_SUBBANDS_8			1 + +#define BT_A2DP_ALLOCATION_SNR			(1 << 1) +#define BT_A2DP_ALLOCATION_LOUDNESS		1 + +#define BT_MPEG_SAMPLING_FREQ_16000		(1 << 5) +#define BT_MPEG_SAMPLING_FREQ_22050		(1 << 4) +#define BT_MPEG_SAMPLING_FREQ_24000		(1 << 3) +#define BT_MPEG_SAMPLING_FREQ_32000		(1 << 2) +#define BT_MPEG_SAMPLING_FREQ_44100		(1 << 1) +#define BT_MPEG_SAMPLING_FREQ_48000		1 + +#define BT_MPEG_LAYER_1				(1 << 2) +#define BT_MPEG_LAYER_2				(1 << 1) +#define BT_MPEG_LAYER_3				1 + +typedef struct { +	uint8_t channel_mode; +	uint8_t frequency; +	uint8_t allocation_method; +	uint8_t subbands; +	uint8_t block_length; +	uint8_t min_bitpool; +	uint8_t max_bitpool; +} __attribute__ ((packed)) sbc_capabilities_t; + +typedef struct { +	uint8_t channel_mode; +	uint8_t crc; +	uint8_t layer; +	uint8_t frequency; +	uint8_t mpf; +	uint16_t bitrate; +} __attribute__ ((packed)) mpeg_capabilities_t; + +struct bt_getcapabilities_rsp { +	bt_audio_rsp_msg_header_t	rsp_h; +	uint8_t				transport;	   /* Granted transport */ +	sbc_capabilities_t		sbc_capabilities;  /* A2DP only */ +	mpeg_capabilities_t		mpeg_capabilities; /* A2DP only */ +	uint16_t			sampling_rate;	   /* SCO only */ +} __attribute__ ((packed)); + +/* BT_SETCONFIGURATION_REQ */ +struct bt_setconfiguration_req { +	bt_audio_msg_header_t	h; +	char			device[18];		/* Address of the remote Device */ +	uint8_t			transport;		/* Requested transport */ +	uint8_t			access_mode;		/* Requested access mode */ +	sbc_capabilities_t	sbc_capabilities;	/* A2DP only - only one of this field +							and next one must be filled */ +	mpeg_capabilities_t	mpeg_capabilities;	/* A2DP only */ +} __attribute__ ((packed)); + +/* BT_SETCONFIGURATION_RSP */ +struct bt_setconfiguration_rsp { +	bt_audio_rsp_msg_header_t	rsp_h; +	uint8_t				transport;	/* Granted transport */ +	uint8_t				access_mode;	/* Granted access mode */ +	uint16_t			link_mtu;	/* Max length that transport supports */ +} __attribute__ ((packed)); + +/* BT_STREAMSTART_REQ */ +#define BT_STREAM_ACCESS_READ		0 +#define BT_STREAM_ACCESS_WRITE		1 +#define BT_STREAM_ACCESS_READWRITE	2 +struct bt_streamstart_req { +	bt_audio_msg_header_t	h; +} __attribute__ ((packed)); + +/* BT_STREAMSTART_RSP */ +struct bt_streamstart_rsp { +	bt_audio_rsp_msg_header_t	rsp_h; +} __attribute__ ((packed)); + +/* BT_STREAMFD_IND */ +/* This message is followed by one byte of data containing the stream data fd +   as ancilliary data */ +struct bt_streamfd_ind { +	bt_audio_msg_header_t	h; +} __attribute__ ((packed)); + +/* BT_STREAMSTOP_REQ */ +struct bt_streamstop_req { +	bt_audio_msg_header_t	h; +} __attribute__ ((packed)); + +/* BT_STREAMSTOP_RSP */ +struct bt_streamstop_rsp { +	bt_audio_rsp_msg_header_t	rsp_h; +} __attribute__ ((packed)); + +/* BT_STREAMSUSPEND_IND */ +struct bt_streamsuspend_ind { +	bt_audio_msg_header_t	h; +} __attribute__ ((packed)); + +/* BT_STREAMRESUME_IND */ +struct bt_streamresume_ind { +	bt_audio_msg_header_t	h; +} __attribute__ ((packed)); + +/* BT_CONTROL_REQ */ + +#define BT_CONTROL_KEY_POWER			0x40 +#define BT_CONTROL_KEY_VOL_UP			0x41 +#define BT_CONTROL_KEY_VOL_DOWN			0x42 +#define BT_CONTROL_KEY_MUTE			0x43 +#define BT_CONTROL_KEY_PLAY			0x44 +#define BT_CONTROL_KEY_STOP			0x45 +#define BT_CONTROL_KEY_PAUSE			0x46 +#define BT_CONTROL_KEY_RECORD			0x47 +#define BT_CONTROL_KEY_REWIND			0x48 +#define BT_CONTROL_KEY_FAST_FORWARD		0x49 +#define BT_CONTROL_KEY_EJECT			0x4A +#define BT_CONTROL_KEY_FORWARD			0x4B +#define BT_CONTROL_KEY_BACKWARD			0x4C + +struct bt_control_req { +	bt_audio_msg_header_t	h; +	uint8_t			mode;		/* Control Mode */ +	uint8_t			key;		/* Control Key */ +} __attribute__ ((packed)); + +/* BT_CONTROL_RSP */ +struct bt_control_rsp { +	bt_audio_rsp_msg_header_t	rsp_h; +	uint8_t				mode;	/* Control Mode */ +	uint8_t				key;	/* Control Key */ +} __attribute__ ((packed)); + +/* BT_CONTROL_IND */ +struct bt_control_ind { +	bt_audio_msg_header_t	h; +	uint8_t			mode;		/* Control Mode */ +	uint8_t			key;		/* Control Key */ +} __attribute__ ((packed)); + +/* Function declaration */ + +/* Opens a connection to the audio service: return a socket descriptor */ +int bt_audio_service_open(void); + +/* Closes a connection to the audio service */ +int bt_audio_service_close(int sk); + +/* Receives stream data file descriptor : must be called after a +BT_STREAMFD_IND message is returned */ +int bt_audio_service_get_data_fd(int sk); + +/* Human readable message type string */ +const char *bt_audio_strmsg(int type); + +#ifdef __cplusplus +} +#endif + +#endif /* BT_AUDIOCLIENT_H */ diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c new file mode 100644 index 00000000..3460fe9a --- /dev/null +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -0,0 +1,922 @@ +/*** +    This file is part of PulseAudio. + +    Copyright 2008 Joao Paulo Rechi Vita + +    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 <string.h> +#include <errno.h> +#include <poll.h> +#include <sys/ioctl.h> +#include <linux/sockios.h> +#include <arpa/inet.h> + +#include <pulse/xmalloc.h> +#include <pulse/timeval.h> +#include <pulse/sample.h> +#include <pulsecore/module.h> +#include <pulsecore/modargs.h> +#include <pulsecore/core-util.h> +#include <pulsecore/core-error.h> +#include <pulsecore/socket-util.h> +#include <pulsecore/thread.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/rtpoll.h> +#include <pulsecore/time-smoother.h> +#include <pulsecore/rtclock.h> + +#include "../dbus-util.h" +#include "module-bluetooth-device-symdef.h" +#include "ipc.h" +#include "sbc.h" +#include "rtp.h" + +#define DEFAULT_SINK_NAME "bluetooth_sink" +#define BUFFER_SIZE 2048 +#define MAX_BITPOOL 64 +#define MIN_BITPOOL 2U +#define SOL_SCO 17 +#define SCO_TXBUFS 0x03 +#define SCO_RXBUFS 0x04 + +PA_MODULE_AUTHOR("Joao Paulo Rechi Vita"); +PA_MODULE_DESCRIPTION("Bluetooth audio sink and source"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(FALSE); +PA_MODULE_USAGE( +        "sink_name=<name of the device> " +        "address=<address of the device> " +        "profile=<a2dp|hsp>"); + +struct bt_a2dp { +    sbc_capabilities_t sbc_capabilities; +    sbc_t sbc;                           /* Codec data */ +    pa_bool_t sbc_initialized;           /* Keep track if the encoder is initialized */ +    size_t codesize;                     /* SBC codesize */ +    unsigned samples;                    /* Number of encoded samples */ +    uint8_t buffer[BUFFER_SIZE];         /* Codec transfer buffer */ +    size_t count;                        /* Codec transfer buffer counter */ + +    unsigned total_samples;              /* Cumulative number of codec samples */ +    uint16_t seq_num;                    /* Cumulative packet sequence */ +    unsigned frame_count;                /* Current frames in buffer*/ +}; + +struct userdata { +    pa_core *core; +    pa_module *module; +    pa_sink *sink; + +    pa_thread_mq thread_mq; +    pa_rtpoll *rtpoll; +    pa_rtpoll_item *rtpoll_item; +    pa_thread *thread; + +    uint64_t offset; +    pa_smoother *smoother; + +    char *name; +    char *addr; +    char *profile; +    pa_sample_spec ss; + +    int audioservice_fd; +    int stream_fd; + +    uint8_t transport; +    char *strtransport; +    size_t link_mtu; +    size_t block_size; +    pa_usec_t latency; + +    struct bt_a2dp a2dp; +}; + +static const char* const valid_modargs[] = { +    "sink_name", +    "address", +    "profile", +    "rate", +    "channels", +    NULL +}; + +static int bt_audioservice_send(int sk, const bt_audio_msg_header_t *msg) { +    int e; +    pa_log_debug("sending %s", bt_audio_strmsg(msg->msg_type)); +    if (send(sk, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0) +        e = 0; +    else { +        e = -errno; +        pa_log_error("Error sending data to audio service: %s(%d)", pa_cstrerror(errno), errno); +    } +    return e; +} + +static int bt_audioservice_recv(int sk, bt_audio_msg_header_t *inmsg) { +    int e; +    const char *type; + +    pa_log_debug("trying to receive msg from audio service..."); +    if (recv(sk, inmsg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0) { +        type = bt_audio_strmsg(inmsg->msg_type); +        if (type) { +            pa_log_debug("Received %s", type); +            e = 0; +        } +        else { +            e = -EINVAL; +            pa_log_error("Bogus message type %d received from audio service", inmsg->msg_type); +        } +    } +    else { +        e = -errno; +        pa_log_error("Error receiving data from audio service: %s(%d)", pa_cstrerror(errno), errno); +    } + +    return e; +} + +static int bt_audioservice_expect(int sk, bt_audio_msg_header_t *rsp_hdr, int expected_type) { +    int e = bt_audioservice_recv(sk, rsp_hdr); +    if (e == 0) { +        if (rsp_hdr->msg_type != expected_type) { +            e = -EINVAL; +            pa_log_error("Bogus message %s received while %s was expected", bt_audio_strmsg(rsp_hdr->msg_type), +                    bt_audio_strmsg(expected_type)); +        } +    } +    return e; +} + +static int bt_getcaps(struct userdata *u) { +    int e; +    union { +        bt_audio_rsp_msg_header_t rsp_hdr; +        struct bt_getcapabilities_req getcaps_req; +        struct bt_getcapabilities_rsp getcaps_rsp; +        uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE]; +    } msg; + +    memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE); +    msg.getcaps_req.h.msg_type = BT_GETCAPABILITIES_REQ; +    strncpy(msg.getcaps_req.device, u->addr, 18); +    if (strcasecmp(u->profile, "a2dp") == 0) +        msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP; +    else if (strcasecmp(u->profile, "hsp") == 0) +        msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_SCO; +    else { +        pa_log_error("Invalid profile argument: %s", u->profile); +        return -1; +    } +    msg.getcaps_req.flags = BT_FLAG_AUTOCONNECT; + +    e = bt_audioservice_send(u->audioservice_fd, &msg.getcaps_req.h); +    if (e < 0) { +        pa_log_error("Failed to send GETCAPABILITIES_REQ"); +        return e; +    } + +    e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_GETCAPABILITIES_RSP); +    if (e < 0) { +        pa_log_error("Failed to expect for GETCAPABILITIES_RSP"); +        return e; +    } +    if (msg.rsp_hdr.posix_errno != 0) { +        pa_log_error("BT_GETCAPABILITIES failed : %s (%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno); +        return -msg.rsp_hdr.posix_errno; +    } + +    if ((u->transport = msg.getcaps_rsp.transport) == BT_CAPABILITIES_TRANSPORT_A2DP) +        u->a2dp.sbc_capabilities = msg.getcaps_rsp.sbc_capabilities; + +    return 0; +} + +static uint8_t default_bitpool(uint8_t freq, uint8_t mode) { +    switch (freq) { +        case BT_SBC_SAMPLING_FREQ_16000: +        case BT_SBC_SAMPLING_FREQ_32000: +            return 53; +        case BT_SBC_SAMPLING_FREQ_44100: +            switch (mode) { +                case BT_A2DP_CHANNEL_MODE_MONO: +                case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: +                    return 31; +                case BT_A2DP_CHANNEL_MODE_STEREO: +                case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: +                    return 53; +                default: +                    pa_log_warn("Invalid channel mode %u", mode); +                    return 53; +            } +        case BT_SBC_SAMPLING_FREQ_48000: +            switch (mode) { +                case BT_A2DP_CHANNEL_MODE_MONO: +                case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: +                    return 29; +                case BT_A2DP_CHANNEL_MODE_STEREO: +                case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: +                    return 51; +                default: +                    pa_log_warn("Invalid channel mode %u", mode); +                    return 51; +            } +        default: +            pa_log_warn("Invalid sampling freq %u", freq); +            return 53; +    } +} + +static int bt_a2dp_init(struct userdata *u) { +    sbc_capabilities_t *cap = &u->a2dp.sbc_capabilities; +    uint8_t max_bitpool, min_bitpool; +    unsigned i; + +    static const struct { +        uint32_t rate; +        uint8_t cap; +    } freq_table[] = { +        { 16000U, BT_SBC_SAMPLING_FREQ_16000 }, +        { 32000U, BT_SBC_SAMPLING_FREQ_32000 }, +        { 44100U, BT_SBC_SAMPLING_FREQ_44100 }, +        { 48000U, BT_SBC_SAMPLING_FREQ_48000 } +    }; + +    /* Find the lowest freq that is at least as high as the requested +     * sampling rate */ +    for (i = 0; i < PA_ELEMENTSOF(freq_table); i++) +        if (freq_table[i].rate >= u->ss.rate || i == PA_ELEMENTSOF(freq_table)-1 ) { +            u->ss.rate = freq_table[i].rate; +            cap->frequency = freq_table[i].cap; +            break; +        } + +    if (u->ss.channels >= 2) { +        if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) +            cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; +        else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) +            cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; +        else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) +            cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; + +        u->ss.channels = 2; +    } else { +        if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) +            cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; +    } + +    if (!cap->channel_mode) { +        pa_log_error("No supported channel modes"); +        return -1; +    } + +    if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16) +        cap->block_length = BT_A2DP_BLOCK_LENGTH_16; +    else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12) +        cap->block_length = BT_A2DP_BLOCK_LENGTH_12; +    else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8) +        cap->block_length = BT_A2DP_BLOCK_LENGTH_8; +    else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4) +        cap->block_length = BT_A2DP_BLOCK_LENGTH_4; +    else { +        pa_log_error("No supported block lengths"); +        return -1; +    } + +    if (cap->subbands & BT_A2DP_SUBBANDS_8) +        cap->subbands = BT_A2DP_SUBBANDS_8; +    else if (cap->subbands & BT_A2DP_SUBBANDS_4) +        cap->subbands = BT_A2DP_SUBBANDS_4; +    else { +        pa_log_error("No supported subbands"); +        return -1; +    } + +    if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) +        cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; +    else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR) +        cap->allocation_method = BT_A2DP_ALLOCATION_SNR; + +    min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool); +    max_bitpool = (uint8_t) PA_MIN(default_bitpool(cap->frequency, cap->channel_mode), cap->max_bitpool); + +    cap->min_bitpool = (uint8_t) min_bitpool; +    cap->max_bitpool = (uint8_t) max_bitpool; + +    return 0; +} + +static void bt_a2dp_setup(struct bt_a2dp *a2dp) { +    sbc_capabilities_t active_capabilities = a2dp->sbc_capabilities; + +    if (a2dp->sbc_initialized) +        sbc_reinit(&a2dp->sbc, 0); +    else +        sbc_init(&a2dp->sbc, 0); +    a2dp->sbc_initialized = TRUE; + +    if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_16000) +        a2dp->sbc.frequency = SBC_FREQ_16000; + +    if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_32000) +        a2dp->sbc.frequency = SBC_FREQ_32000; + +    if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_44100) +        a2dp->sbc.frequency = SBC_FREQ_44100; + +    if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_48000) +        a2dp->sbc.frequency = SBC_FREQ_48000; + +    if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO) +        a2dp->sbc.mode = SBC_MODE_MONO; + +    if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) +        a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL; + +    if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) +        a2dp->sbc.mode = SBC_MODE_STEREO; + +    if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) +        a2dp->sbc.mode = SBC_MODE_JOINT_STEREO; + +    a2dp->sbc.allocation = (uint8_t) (active_capabilities.allocation_method == BT_A2DP_ALLOCATION_SNR ? SBC_AM_SNR : SBC_AM_LOUDNESS); + +    switch (active_capabilities.subbands) { +        case BT_A2DP_SUBBANDS_4: +            a2dp->sbc.subbands = SBC_SB_4; +            break; +        case BT_A2DP_SUBBANDS_8: +            a2dp->sbc.subbands = SBC_SB_8; +            break; +    } + +    switch (active_capabilities.block_length) { +        case BT_A2DP_BLOCK_LENGTH_4: +            a2dp->sbc.blocks = SBC_BLK_4; +            break; +        case BT_A2DP_BLOCK_LENGTH_8: +            a2dp->sbc.blocks = SBC_BLK_8; +            break; +        case BT_A2DP_BLOCK_LENGTH_12: +            a2dp->sbc.blocks = SBC_BLK_12; +            break; +        case BT_A2DP_BLOCK_LENGTH_16: +            a2dp->sbc.blocks = SBC_BLK_16; +            break; +    } + +    a2dp->sbc.bitpool = active_capabilities.max_bitpool; +    a2dp->codesize = (uint16_t) sbc_get_codesize(&a2dp->sbc); +    a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); +} + +static int bt_setconf(struct userdata *u) { +    int e; +    union { +        bt_audio_rsp_msg_header_t rsp_hdr; +        struct bt_setconfiguration_req setconf_req; +        struct bt_setconfiguration_rsp setconf_rsp; +        uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE]; +    } msg; + +    if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) { +        e = bt_a2dp_init(u); +        if (e < 0) { +            pa_log_error("a2dp_init error"); +            return e; +        } +        u->ss.format = PA_SAMPLE_S16LE; +    } +    else +        u->ss.format = PA_SAMPLE_U8; + +    memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE); +    msg.setconf_req.h.msg_type = BT_SETCONFIGURATION_REQ; +    strncpy(msg.setconf_req.device, u->addr, 18); +    msg.setconf_req.transport = u->transport; +    if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) +        msg.setconf_req.sbc_capabilities = u->a2dp.sbc_capabilities; +    msg.setconf_req.access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; + +    e = bt_audioservice_send(u->audioservice_fd, &msg.setconf_req.h); +    if (e < 0) { +        pa_log_error("Failed to send BT_SETCONFIGURATION_REQ"); +        return e; +    } + +    e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_SETCONFIGURATION_RSP); +    if (e < 0) { +        pa_log_error("Failed to expect BT_SETCONFIGURATION_RSP"); +        return e; +    } + +    if (msg.rsp_hdr.posix_errno != 0) { +        pa_log_error("BT_SETCONFIGURATION failed : %s(%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno); +        return -msg.rsp_hdr.posix_errno; +    } + +    u->transport = msg.setconf_rsp.transport; +    u->strtransport = (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP ? pa_xstrdup("A2DP") : pa_xstrdup("SCO")); +    u->link_mtu = msg.setconf_rsp.link_mtu; + +    /* setup SBC encoder now we agree on parameters */ +    if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) { +        bt_a2dp_setup(&u->a2dp); +        u->block_size = u->a2dp.codesize; +        pa_log_info("sbc parameters:\n\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", +                u->a2dp.sbc.allocation, u->a2dp.sbc.subbands, u->a2dp.sbc.blocks, u->a2dp.sbc.bitpool); +    } +    else +        u->block_size = u->link_mtu; + +    return 0; +} + +static int bt_getstreamfd(struct userdata *u) { +    int e; +//    uint32_t period_count = io->buffer_size / io->period_size; +    union { +        bt_audio_rsp_msg_header_t rsp_hdr; +        struct bt_streamstart_req start_req; +        struct bt_streamfd_ind streamfd_ind; +        uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE]; +    } msg; + +    memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE); +    msg.start_req.h.msg_type = BT_STREAMSTART_REQ; + +    e = bt_audioservice_send(u->audioservice_fd, &msg.start_req.h); +    if (e < 0) { +        pa_log_error("Failed to send BT_STREAMSTART_REQ"); +        return e; +    } + +    e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_STREAMSTART_RSP); +    if (e < 0) { +        pa_log_error("Failed to expect BT_STREAMSTART_RSP"); +        return e; +    } + +    if (msg.rsp_hdr.posix_errno != 0) { +        pa_log_error("BT_START failed : %s(%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno); +        return -msg.rsp_hdr.posix_errno; +    } + +    e = bt_audioservice_expect(u->audioservice_fd, &msg.streamfd_ind.h, BT_STREAMFD_IND); +    if (e < 0) { +        pa_log_error("Failed to expect BT_STREAMFD_IND"); +        return e; +    } + +    if (u->stream_fd >= 0) +        pa_close(u->stream_fd); + +    u->stream_fd = bt_audio_service_get_data_fd(u->audioservice_fd); +    if (u->stream_fd < 0) { +        pa_log_error("Failed to get data fd: %s (%d)",pa_cstrerror(errno), errno); +        return -errno; +    } + +//   if (setsockopt(u->stream_fd, SOL_SCO, SCO_TXBUFS, &period_count, sizeof(period_count)) == 0) +//       return 0; +//   if (setsockopt(u->stream_fd, SOL_SCO, SO_SNDBUF, &period_count, sizeof(period_count)) == 0) +//       return 0; +//   /* FIXME : handle error codes */ +    pa_make_fd_nonblock(u->stream_fd); +//    pa_make_socket_low_delay(u->stream_fd); + +    return 0; +} + +static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { +    struct userdata *u = PA_SINK(o)->userdata; + +    pa_log_debug("got message: %d", code); +    switch (code) { + +        case PA_SINK_MESSAGE_SET_STATE: +            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { +                case PA_SINK_SUSPENDED: +                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state)); +                    pa_smoother_pause(u->smoother, pa_rtclock_usec()); +                    break; +                case PA_SINK_IDLE: +                case PA_SINK_RUNNING: +                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) +                        pa_smoother_resume(u->smoother, pa_rtclock_usec()); +                    break; +                case PA_SINK_UNLINKED: +                case PA_SINK_INIT: +                    ; +            } +            break; + +        case PA_SINK_MESSAGE_GET_LATENCY: { +            pa_usec_t w, r; +/*             r = pa_smoother_get(u->smoother, pa_rtclock_usec()); */ +/* /\*             w = pa_bytes_to_usec(u->offset + (uint64_t) u->memchunk.length, &u->sink->sample_spec); *\/ */ +            *((pa_usec_t*) data) = /*w > r ? w - r :*/ 0; +            return 0; +        } + +    } + +    return pa_sink_process_msg(o, code, data, offset, chunk); +} + +static int sco_process_render(struct userdata *u) { +    void *p; +    int ret = 0; +    pa_memchunk memchunk; + +    pa_sink_render_full(u->sink, u->block_size, &memchunk); + +    p = pa_memblock_acquire(memchunk.memblock); + +    for (;;) { +        ssize_t l; + +        l = pa_loop_write(u->stream_fd, (uint8_t*) p, memchunk.length, NULL); +        pa_log_debug("Memblock written to socket: %li bytes", (long) l); + +        pa_assert(l != 0); + +        if (l > 0) { +            u->offset += (uint64_t) l; +            break; +        } + +        if (errno == EINTR) +            pa_log_debug("EINTR"); +        else if (errno == EAGAIN) +            pa_log_debug("EAGAIN"); +        else { +            pa_log_error("Failed to write data to FIFO: %s", pa_cstrerror(errno)); +            ret = -1; +            break; +        } +    } + +    pa_memblock_release(memchunk.memblock); +    pa_memblock_unref(memchunk.memblock); + +    return ret; +} + +static int a2dp_process_render(struct userdata *u) { +    int written; + +    struct bt_a2dp *a2dp = &u->a2dp; +    struct rtp_header *header = (void *) a2dp->buffer; +    struct rtp_payload *payload = (void *) (a2dp->buffer + sizeof(*header)); + +    pa_assert(u); + +    do { +        /* Render some data */ +        int frame_size, encoded; +        void *p; +        pa_memchunk memchunk; + +        pa_sink_render_full(u->sink, u->block_size, &memchunk); + +        p = pa_memblock_acquire(memchunk.memblock); + +        frame_size = (uint16_t) sbc_get_frame_length(&a2dp->sbc); +        pa_log_debug("SBC frame_size: %d", frame_size); + +        encoded = sbc_encode(&a2dp->sbc, p, (int) a2dp->codesize, a2dp->buffer + a2dp->count, +                             (int) (sizeof(a2dp->buffer) - a2dp->count), &written); +        pa_log_debug("SBC: encoded: %d; written: %d", encoded, written); + +        pa_memblock_release(memchunk.memblock); +        pa_memblock_unref(memchunk.memblock); + +        if (encoded <= 0) { +            pa_log_error("SBC encoding error (%d)", encoded); +            return -1; +        } + +        a2dp->count += (size_t) written; +        a2dp->frame_count++; +        a2dp->samples += (unsigned) encoded / frame_size; +        a2dp->total_samples += (unsigned) encoded / frame_size; + +    } while (a2dp->count + (size_t) written <= u->link_mtu); + +    /* write it to the fifo */ +    memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload)); +    payload->frame_count = a2dp->frame_count; +    header->v = 2; +    header->pt = 1; +    header->sequence_number = htons(a2dp->seq_num); +    header->timestamp = htonl(a2dp->total_samples); +    header->ssrc = htonl(1); + +    for (;;) { +        ssize_t l; + +        l = pa_loop_write(u->stream_fd, a2dp->buffer, a2dp->count, NULL); +        pa_log_debug("avdtp_write: requested %lu bytes; written %li bytes", (unsigned long) a2dp->count, (long) l); + +        pa_assert(l != 0); + +        if (l > 0) +            break; + +        if (errno == EINTR) +            pa_log_debug("EINTR"); +        else if (errno == EAGAIN) +            pa_log_debug("EAGAIN"); +        else { +            pa_log_error("Failed to write data to FIFO: %s", pa_cstrerror(errno)); +            return -1; +        } +    } + +    u->offset += a2dp->codesize*a2dp->frame_count; + +    /* Reset buffer of data to send */ +    a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); +    a2dp->frame_count = 0; +    a2dp->samples = 0; +    a2dp->seq_num++; + +    return 0; +} + +static void thread_func(void *userdata) { +    struct userdata *u = userdata; + +    pa_assert(u); + +    pa_log_debug("IO Thread starting up"); + +    if (u->core->realtime_scheduling) +        pa_make_realtime(u->core->realtime_priority); + +    pa_thread_mq_install(&u->thread_mq); +    pa_rtpoll_install(u->rtpoll); + +    pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); + +    for (;;) { +        int ret, l; +        struct pollfd *pollfd; +        uint64_t n; +        pa_usec_t usec; + +        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) +            if (u->sink->thread_info.rewind_requested) +                pa_sink_process_rewind(u->sink, 0); + +        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + +        if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) { +            if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) { +                if ((l = a2dp_process_render(u)) < 0) +                    goto fail; +            } else { +                if ((l = sco_process_render(u)) < 0) +                    goto fail; +            } +            pollfd->revents = 0; + +            /* feed the time smoother */ +            n = u->offset; +            if (ioctl(u->stream_fd, SIOCOUTQ, &l) >= 0 && l > 0) +                n -= (uint64_t) l; +            usec = pa_bytes_to_usec(n, &u->sink->sample_spec); +            if (usec > u->latency) +                usec -= u->latency; +            else +                usec = 0; +            pa_smoother_put(u->smoother, pa_rtclock_usec(), usec); +        } + +        /* Hmm, nothing to do. Let's sleep */ +        pa_log_debug("IO thread going to sleep"); +        pollfd->events = (short) (PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0); +        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) { +            pa_log_error("rtpoll_run < 0"); +            goto fail; +        } +        pa_log_debug("IO thread waking up"); + +        if (ret == 0) { +            pa_log_debug("rtpoll_run == 0"); +            goto finish; +        } + +        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); +        if (pollfd->revents & ~POLLOUT) { +            pa_log_error("FIFO shutdown."); +            goto fail; +        } +    } + +fail: +    /* If this was no regular exit from the loop we have to continue processing messages until we receive PA_MESSAGE_SHUTDOWN */ +    pa_log_debug("IO thread failed"); +    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); +    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); + +finish: +    pa_log_debug("IO thread shutting down"); +} + +int pa__init(pa_module* m) { +    int e; +    pa_modargs *ma; +    uint32_t channels; +    pa_sink_new_data data; +    struct pollfd *pollfd; +    struct userdata *u; + +    pa_assert(m); +    m->userdata = u = pa_xnew0(struct userdata, 1); +    u->module = m; +    u->core = m->core; +    u->audioservice_fd = -1; +    u->stream_fd = -1; +    u->transport = (uint8_t) -1; +    u->offset = 0; +    u->latency = 0; +    u->a2dp.sbc_initialized = FALSE; +    u->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->rtpoll_item = NULL; +    u->ss = m->core->default_sample_spec; + +    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { +        pa_log_error("Failed to parse module arguments"); +        goto fail; +    } +    if (!(u->name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)))) { +        pa_log_error("Failed to get device name from module arguments"); +        goto fail; +    } +    if (!(u->addr = pa_xstrdup(pa_modargs_get_value(ma, "address", NULL)))) { +        pa_log_error("Failed to get device address from module arguments"); +        goto fail; +    } +    if (!(u->profile = pa_xstrdup(pa_modargs_get_value(ma, "profile", NULL)))) { +        pa_log_error("Failed to get profile from module arguments"); +        goto fail; +    } +    if (pa_modargs_get_value_u32(ma, "rate", &u->ss.rate) < 0) { +        pa_log_error("Failed to get rate from module arguments"); +        goto fail; +    } + +    channels = u->ss.channels; +    if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0) { +        pa_log_error("Failed to get channels from module arguments"); +        goto fail; +    } +    u->ss.channels = (uint8_t) channels; + +    /* connect to the bluez audio service */ +    u->audioservice_fd = bt_audio_service_open(); +    if (u->audioservice_fd <= 0) { +        pa_log_error("Couldn't connect to bluetooth audio service"); +        goto fail; +    } +    pa_log_debug("Connected to the bluetooth audio service"); + +    /* queries device capabilities */ +    e = bt_getcaps(u); +    if (e < 0) { +        pa_log_error("Failed to get device capabilities"); +        goto fail; +    } +    pa_log_debug("Got device capabilities"); + +    /* configures the connection */ +    e = bt_setconf(u); +    if (e < 0) { +        pa_log_error("Failed to set config"); +        goto fail; +    } +    pa_log_debug("Connection to the device configured"); + +    /* gets the device socket */ +    e = bt_getstreamfd(u); +    if (e < 0) { +        pa_log_error("Failed to get stream fd (%d)", e); +        goto fail; +    } +    pa_log_debug("Got the device socket"); + +    /* create sink */ +    pa_sink_new_data_init(&data); +    data.driver = __FILE__; +    data.module = m; +    pa_sink_new_data_set_name(&data, u->name); +    pa_sink_new_data_set_sample_spec(&data, &u->ss); +    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->name); +    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Bluetooth %s '%s' (%s)", u->strtransport, u->name, u->addr); +    pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile); +    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_API, "bluez"); +    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_CLASS, "sound"); +    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_CONNECTOR, "bluetooth"); +/*     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, "headset"); /\*FIXME*\/ */ +/*     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_VENDOR_PRODUCT_ID, "product_id"); /\*FIXME*\/ */ +/*     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_SERIAL, "serial"); /\*FIXME*\/ */ +    u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); +    pa_sink_new_data_done(&data); +    if (!u->sink) { +        pa_log_error("Failed to create sink"); +        goto fail; +    } +    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->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; +    pollfd->events = pollfd->revents = 0; + +    /* start rt thread */ +    if (!(u->thread = pa_thread_new(thread_func, u))) { +        pa_log_error("Failed to create IO thread"); +        goto fail; +    } +    pa_sink_put(u->sink); + +    pa_modargs_free(ma); +    return 0; + +fail: +    if (ma) +        pa_modargs_free(ma); + +    pa__done(m); +    return -1; +} + +void pa__done(pa_module *m) { +    struct userdata *u; +    pa_assert(m); + +    if (!(u = m->userdata)) +        return; + +    if (u->sink) +        pa_sink_unlink(u->sink); + +    if (u->thread) { +        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); +        pa_thread_free(u->thread); +    } + +    if (u->sink) +        pa_sink_unref(u->sink); + +    pa_thread_mq_done(&u->thread_mq); + +    if (u->rtpoll_item) +        pa_rtpoll_item_free(u->rtpoll_item); + +    if (u->rtpoll) +        pa_rtpoll_free(u->rtpoll); + +    if (u->smoother) +        pa_smoother_free(u->smoother); + +    pa_xfree(u->name); +    pa_xfree(u->addr); +    pa_xfree(u->profile); +    pa_xfree(u->strtransport); + +    if (u->stream_fd >= 0) +        pa_close(u->stream_fd); + +    if (u->audioservice_fd >= 0) +        pa_close(u->audioservice_fd); + +    pa_xfree(u); +} diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c new file mode 100644 index 00000000..36c0a35e --- /dev/null +++ b/src/modules/bluetooth/module-bluetooth-discover.c @@ -0,0 +1,558 @@ +/*** +    This file is part of PulseAudio. + +    Copyright 2008 Joao Paulo Rechi Vita + +    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 <pulse/xmalloc.h> +#include <pulsecore/module.h> +#include <pulsecore/modargs.h> +#include <pulsecore/macro.h> +#include <pulsecore/llist.h> +#include <pulsecore/core-util.h> + +#include "../dbus-util.h" +#include "module-bluetooth-discover-symdef.h" + +PA_MODULE_AUTHOR("Joao Paulo Rechi Vita"); +PA_MODULE_DESCRIPTION("Detect available bluetooth audio devices and load bluetooth audio drivers"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_USAGE(""); + +struct module { +    char *profile; +    pa_module *pa_m; +    PA_LLIST_FIELDS(struct module); +}; + +struct uuid { +    char *uuid; +    PA_LLIST_FIELDS(struct uuid); +}; + +struct device { +    char *name; +    char *object_path; +    int paired; +    char *alias; +    int connected; +    PA_LLIST_HEAD(struct uuid, uuid_list); +    char *address; +    int class; +    int trusted; +    PA_LLIST_HEAD(struct module, module_list); +    PA_LLIST_FIELDS(struct device); +}; + +struct userdata { +    pa_module *module; +    pa_dbus_connection *conn; +    PA_LLIST_HEAD(struct device, device_list); +}; + +static struct module *module_new(const char *profile, pa_module *pa_m) { +    struct module *m; + +    m = pa_xnew(struct module, 1); +    m->profile = pa_xstrdup(profile); +    m->pa_m = pa_m; +    PA_LLIST_INIT(struct module, m); + +    return m; +} + +static void module_free(struct module *m) { +    pa_assert(m); + +    pa_xfree(m->profile); +    pa_xfree(m); +} + +static struct module* module_find(struct device *d, const char *profile) { +    struct module *m; + +    for (m = d->module_list; d; d = d->next) +        if (pa_streq(m->profile, profile)) +            return m; + +    return NULL; +} + +static struct uuid *uuid_new(const char *uuid) { +    struct uuid *node; + +    node = pa_xnew(struct uuid, 1); +    node->uuid = pa_xstrdup(uuid); +    PA_LLIST_INIT(struct uuid, node); + +    return node; +} + +static void uuid_free(struct uuid *uuid) { +    pa_assert(uuid); + +    pa_xfree(uuid->uuid); +    pa_xfree(uuid); +} + +static struct device *device_new(const char *object_path) { +    struct device *node; + +    node = pa_xnew(struct device, 1); +    node->name = NULL; +    node->object_path = pa_xstrdup(object_path); +    node->paired = -1; +    node->alias = NULL; +    node->connected = -1; +    PA_LLIST_HEAD_INIT(struct uuid, node->uuid_list); +    node->address = NULL; +    node->class = -1; +    node->trusted = -1; +    PA_LLIST_HEAD_INIT(struct module, node->module_list); +    PA_LLIST_INIT(struct device, node); + +    return node; +} + +static void device_free(struct device *device) { +    struct module *m; +    struct uuid *i; + +    pa_assert(device); + +    while ((m = device->module_list)) { +        PA_LLIST_REMOVE(struct module, device->module_list, m); +        module_free(m); +    } + +    while ((i = device->uuid_list)) { +        PA_LLIST_REMOVE(struct uuid, device->uuid_list, i); +        uuid_free(i); +    } + +    pa_xfree(device->name); +    pa_xfree(device->object_path); +    pa_xfree(device->alias); +    pa_xfree(device->address); +    pa_xfree(device); +} + +static struct device* device_find(struct userdata *u, const char *path) { +    struct device *i; + +    for (i = u->device_list; i; i = i->next) +        if (pa_streq(i->object_path, path)) +            return i; + +    return NULL; +} + +static int parse_device_property(struct userdata *u, struct device *d, DBusMessageIter *i) { +    const char *key; +    DBusMessageIter variant_i; + +    pa_assert(u); +    pa_assert(d); +    pa_assert(i); + +    if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) { +        pa_log("Property name not a string."); +        return -1; +    } + +    dbus_message_iter_get_basic(i, &key); + +    if (!dbus_message_iter_next(i))  { +        pa_log("Property value missing"); +        return -1; +    } + +    if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) { +        pa_log("Property value not a variant."); +        return -1; +    } + +    dbus_message_iter_recurse(i, &variant_i); + +    pa_log_debug("Parsing device property %s", key); + +    switch (dbus_message_iter_get_arg_type(&variant_i)) { + +        case DBUS_TYPE_STRING: { + +            const char *value; +            dbus_message_iter_get_basic(&variant_i, &value); + +            if (pa_streq(key, "Name")) { +                pa_xfree(d->name); +                d->name = pa_xstrdup(value); +            } else if (pa_streq(key, "Alias")) { +                pa_xfree(d->alias); +                d->alias = pa_xstrdup(value); +            } else if (pa_streq(key, "Address")) { +                pa_xfree(d->address); +                d->address = pa_xstrdup(value); +            } + +            break; +        } + +        case DBUS_TYPE_BOOLEAN: { + +            dbus_bool_t value; +            dbus_message_iter_get_basic(&variant_i, &value); + +            if (pa_streq(key, "Paired")) +                d->paired = !!value; +            else if (pa_streq(key, "Connected")) +                d->connected = !!value; +            else if (pa_streq(key, "Trusted")) +                d->trusted = !!value; + +            break; +        } + +        case DBUS_TYPE_UINT32: { + +            uint32_t value; +            dbus_message_iter_get_basic(&variant_i, &value); + +            if (pa_streq(key, "Class")) +                d->class = (int) value; + +            break; +        } + +        case DBUS_TYPE_ARRAY: { + +            DBusMessageIter ai; +            dbus_message_iter_recurse(&variant_i, &ai); + +            if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && +                pa_streq(key, "UUIDs")) { + +                while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) { +                    struct uuid *node; +                    const char *value; + +                    dbus_message_iter_get_basic(&ai, &value); +                    node = uuid_new(value); +                    PA_LLIST_PREPEND(struct uuid, d->uuid_list, node); + +                    if (!dbus_message_iter_next(&ai)) +                        break; +                } +            } + +            break; +        } +    } + +    return 0; +} + +static int get_device_properties(struct userdata *u, struct device *d) { +    DBusError e; +    DBusMessage *m = NULL, *r = NULL; +    DBusMessageIter arg_i, element_i; +    int ret = -1; + +    pa_assert(u); +    pa_assert(d); + +    dbus_error_init(&e); + +    pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->object_path, "org.bluez.Device", "GetProperties")); + +    r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e); + +    if (!r) { +        pa_log("org.bluez.Device.GetProperties failed: %s", e.message); +        goto finish; +    } + +    if (!dbus_message_iter_init(r, &arg_i)) { +        pa_log("org.bluez.Device.GetProperties reply has no arguments"); +        goto finish; +    } + +    if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) { +        pa_log("org.bluez.Device.GetProperties argument is not an array"); +        goto finish; +    } + +    dbus_message_iter_recurse(&arg_i, &element_i); +    while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) { + +        if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) { +            DBusMessageIter dict_i; + +            dbus_message_iter_recurse(&element_i, &dict_i); + +            if (parse_device_property(u, d, &dict_i) < 0) +                goto finish; +        } + +        if (!dbus_message_iter_next(&element_i)) +            break; +    } + +    ret = 0; + +finish: +    if (m) +        dbus_message_unref(m); +    if (r) +        dbus_message_unref(r); + +    dbus_error_free(&e); + +    return ret; +} + +static void load_module_for_device(struct userdata *u, struct device *d, const char *profile) { +    char *args; +    pa_module *pa_m; +    struct module *m; + +    pa_assert(u); +    pa_assert(d); + +    get_device_properties(u, d); +    args = pa_sprintf_malloc("sink_name=\"%s\" address=\"%s\" profile=\"%s\"", d->name, d->address, profile); +    pa_m = pa_module_load(u->module->core, "module-bluetooth-device", args); +    pa_xfree(args); + +    if (!pa_m) { +        pa_log_debug("Failed to load module for device %s", d->object_path); +        return; +    } + +    m = module_new(profile, pa_m); +    PA_LLIST_PREPEND(struct module, d->module_list, m); +} + +static void unload_module_for_device(struct userdata *u, struct device *d, const char *profile) { +    struct module *m; + +    pa_assert(u); +    pa_assert(d); + +    if (!(m = module_find(d, profile))) +        return; + +    pa_module_unload_request(m->pa_m, TRUE); + +    PA_LLIST_REMOVE(struct module, d->module_list, m); +    module_free(m); +} + +static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) { +    DBusMessageIter arg_i; +    DBusError err; +    const char *value; +    struct userdata *u; + +    pa_assert(bus); +    pa_assert(msg); +    pa_assert(userdata); +    u = userdata; + +    dbus_error_init(&err); + +    pa_log_debug("dbus: interface=%s, path=%s, member=%s\n", +            dbus_message_get_interface(msg), +            dbus_message_get_path(msg), +            dbus_message_get_member(msg)); + +    if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DeviceRemoved")) { + +        if (!dbus_message_iter_init(msg, &arg_i)) +            pa_log("dbus: message has no parameters"); +        else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH) +            pa_log("dbus: argument is not object path"); +        else { +            struct device *d; + +            dbus_message_iter_get_basic(&arg_i, &value); +            pa_log_debug("hcid: device %s removed", value); + +            if ((d = device_find(u, value))) { +                PA_LLIST_REMOVE(struct device, u->device_list, d); +                device_free(d); +            } +        } + +    } else if (dbus_message_is_signal(msg, "org.bluez.Headset", "PropertyChanged") || +               dbus_message_is_signal(msg, "org.bluez.AudioSink", "PropertyChanged")) { + +        struct device *d; +        const char *profile; +        DBusMessageIter variant_i; +        dbus_bool_t connected; + +        if (!dbus_message_iter_init(msg, &arg_i)) { +            pa_log("dbus: message has no parameters"); +            goto done; +        } + +        if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_STRING) { +            pa_log("Property name not a string."); +            goto done; +        } + +        dbus_message_iter_get_basic(&arg_i, &value); + +        if (!pa_streq(value, "Connected")) +            goto done; + +        if (!dbus_message_iter_next(&arg_i)) { +            pa_log("Property value missing"); +            goto done; +        } + +        if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_VARIANT) { +            pa_log("Property value not a variant."); +            goto done; +        } + +        dbus_message_iter_recurse(&arg_i, &variant_i); + +        if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_BOOLEAN) { +            pa_log("Property value not a boolean."); +            goto done; +        } + +        dbus_message_iter_get_basic(&variant_i, &connected); + +        if (dbus_message_is_signal(msg, "org.bluez.Headset", "PropertyChanged")) +            profile = "hsp"; +        else +            profile = "a2dp"; + +        d = device_find(u, dbus_message_get_path(msg)); + +        if (connected) { +            if (!d) { +                    d = device_new(dbus_message_get_path(msg)); +                    PA_LLIST_PREPEND(struct device, u->device_list, d); +            } + +            load_module_for_device(u, d, profile); +        } else if (d) +            unload_module_for_device(u, d, profile); +    } + +done: +    dbus_error_free(&err); +    return DBUS_HANDLER_RESULT_HANDLED; +} + +void pa__done(pa_module* m) { +    struct userdata *u; +    struct device *i; + +    pa_assert(m); + +    if (!(u = m->userdata)) +        return; + +    while ((i = u->device_list)) { +        PA_LLIST_REMOVE(struct device, u->device_list, i); +        device_free(i); +    } + +    if (u->conn) { +        DBusError error; +        dbus_error_init(&error); + +        dbus_bus_remove_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", &error); +        dbus_error_free(&error); + +        dbus_bus_remove_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'", &error); +        dbus_error_free(&error); + +        dbus_bus_remove_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", &error); +        dbus_error_free(&error); + +        dbus_connection_remove_filter(pa_dbus_connection_get(u->conn), filter_cb, u); + +        pa_dbus_connection_unref(u->conn); +    } + +    pa_xfree(u); +} + +int pa__init(pa_module* m) { +    DBusError err; +    struct userdata *u; + +    pa_assert(m); +    dbus_error_init(&err); + +    m->userdata = u = pa_xnew(struct userdata, 1); +    u->module = m; +    PA_LLIST_HEAD_INIT(struct device, u->device_list); + +    /* connect to the bus */ +    u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err); +    if (dbus_error_is_set(&err) || (u->conn == NULL) ) { +        pa_log("Failed to get D-Bus connection: %s", err.message); +        goto fail; +    } + +    /* dynamic detection of bluetooth audio devices */ +    if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) { +        pa_log_error("Failed to add filter function"); +        goto fail; +    } + +    dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", &err); +    if (dbus_error_is_set(&err)) { +        pa_log_error("Unable to subscribe to org.bluez.Adapter signals: %s: %s", err.name, err.message); +        goto fail; +    } + +    dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'", &err); +    if (dbus_error_is_set(&err)) { +        pa_log_error("Unable to subscribe to org.bluez.Headset signals: %s: %s", err.name, err.message); +        goto fail; +    } + +    dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", &err); +    if (dbus_error_is_set(&err)) { +        pa_log_error("Unable to subscribe to org.bluez.AudioSink signals: %s: %s", err.name, err.message); +        goto fail; +    } + +    return 0; + +fail: +    dbus_error_free(&err); +    pa__done(m); + +    return -1; +} diff --git a/src/modules/module-bt-proximity.c b/src/modules/bluetooth/module-bluetooth-proximity.c index f924c3cb..4cfaaf5b 100644 --- a/src/modules/module-bt-proximity.c +++ b/src/modules/bluetooth/module-bluetooth-proximity.c @@ -43,8 +43,8 @@  #include <pulsecore/core-error.h>  #include <pulsecore/start-child.h> -#include "dbus-util.h" -#include "module-bt-proximity-symdef.h" +#include "../dbus-util.h" +#include "module-bluetooth-proximity-symdef.h"  PA_MODULE_AUTHOR("Lennart Poettering");  PA_MODULE_DESCRIPTION("Bluetooth Proximity Volume Control"); diff --git a/src/modules/bt-proximity-helper.c b/src/modules/bluetooth/proximity-helper.c index 3767f01c..3767f01c 100644 --- a/src/modules/bt-proximity-helper.c +++ b/src/modules/bluetooth/proximity-helper.c diff --git a/src/modules/bluetooth/rtp.h b/src/modules/bluetooth/rtp.h new file mode 100644 index 00000000..690bd43a --- /dev/null +++ b/src/modules/bluetooth/rtp.h @@ -0,0 +1,76 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This library is free software; you can redistribute it and/or + *  modify it under the terms of the GNU Lesser General Public + *  License as published by the Free Software Foundation; either + *  version 2.1 of the License, or (at your option) any later version. + * + *  This library is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + *  Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this library; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct rtp_header { +	uint8_t cc:4; +	uint8_t x:1; +	uint8_t p:1; +	uint8_t v:2; + +	uint8_t pt:7; +	uint8_t m:1; + +	uint16_t sequence_number; +	uint32_t timestamp; +	uint32_t ssrc; +	uint32_t csrc[0]; +} __attribute__ ((packed)); + +struct rtp_payload { +	uint8_t frame_count:4; +	uint8_t rfa0:1; +	uint8_t is_last_fragment:1; +	uint8_t is_first_fragment:1; +	uint8_t is_fragmented:1; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct rtp_header { +	uint8_t v:2; +	uint8_t p:1; +	uint8_t x:1; +	uint8_t cc:4; + +	uint8_t m:1; +	uint8_t pt:7; + +	uint16_t sequence_number; +	uint32_t timestamp; +	uint32_t ssrc; +	uint32_t csrc[0]; +} __attribute__ ((packed)); + +struct rtp_payload { +	uint8_t is_fragmented:1; +	uint8_t is_first_fragment:1; +	uint8_t is_last_fragment:1; +	uint8_t rfa0:1; +	uint8_t frame_count:4; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif diff --git a/src/modules/bluetooth/sbc.c b/src/modules/bluetooth/sbc.c new file mode 100644 index 00000000..02a6143d --- /dev/null +++ b/src/modules/bluetooth/sbc.c @@ -0,0 +1,1411 @@ +/* + * + *  Bluetooth low-complexity, subband codec (SBC) library + * + *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org> + *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch> + *  Copyright (C) 2005-2008  Brad Midgley <bmidgley@xmission.com> + * + * + *  This library is free software; you can redistribute it and/or + *  modify it under the terms of the GNU Lesser General Public + *  License as published by the Free Software Foundation; either + *  version 2.1 of the License, or (at your option) any later version. + * + *  This library is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + *  Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this library; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +/* todo items: + +  use a log2 table for byte integer scale factors calculation (sum log2 results +  for high and low bytes) fill bitpool by 16 bits instead of one at a time in +  bits allocation/bitpool generation port to the dsp + +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> + +#include "sbc_math.h" +#include "sbc_tables.h" + +#include "sbc.h" + +#define SBC_SYNCWORD	0x9C + +/* This structure contains an unpacked SBC frame. +   Yes, there is probably quite some unused space herein */ +struct sbc_frame { +	uint8_t frequency; +	uint8_t block_mode; +	uint8_t blocks; +	enum { +		MONO		= SBC_MODE_MONO, +		DUAL_CHANNEL	= SBC_MODE_DUAL_CHANNEL, +		STEREO		= SBC_MODE_STEREO, +		JOINT_STEREO	= SBC_MODE_JOINT_STEREO +	} mode; +	uint8_t channels; +	enum { +		LOUDNESS	= SBC_AM_LOUDNESS, +		SNR		= SBC_AM_SNR +	} allocation; +	uint8_t subband_mode; +	uint8_t subbands; +	uint8_t bitpool; +	uint8_t codesize; +	uint8_t length; + +	/* bit number x set means joint stereo has been used in subband x */ +	uint8_t joint; + +	/* only the lower 4 bits of every element are to be used */ +	uint8_t scale_factor[2][8]; + +	/* raw integer subband samples in the frame */ + +	int32_t sb_sample_f[16][2][8]; +	int32_t sb_sample[16][2][8];	/* modified subband samples */ +	int16_t pcm_sample[2][16*8];	/* original pcm audio samples */ +}; + +struct sbc_decoder_state { +	int subbands; +	int32_t V[2][170]; +	int offset[2][16]; +}; + +struct sbc_encoder_state { +	int subbands; +	int position[2]; +	int32_t X[2][160]; +}; + +/* + * Calculates the CRC-8 of the first len bits in data + */ +static const uint8_t crc_table[256] = { +	0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, +	0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, +	0xCD, 0xD0, 0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E, +	0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76, +	0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE, 0xC9, 0xD4, +	0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C, +	0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19, +	0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1, +	0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40, +	0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8, +	0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D, +	0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65, +	0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7, +	0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F, +	0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A, +	0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2, +	0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75, +	0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D, +	0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8, +	0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50, +	0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2, +	0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A, +	0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F, +	0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7, +	0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66, +	0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E, +	0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB, +	0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43, +	0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1, +	0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09, +	0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C, +	0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4 +}; + +static uint8_t sbc_crc8(const uint8_t *data, size_t len) +{ +	uint8_t crc = 0x0f; +	size_t i; +	uint8_t octet; + +	for (i = 0; i < len / 8; i++) +		crc = crc_table[crc ^ data[i]]; + +	octet = data[i]; +	for (i = 0; i < len % 8; i++) { +		unsigned char bit = ((octet ^ crc) & 0x80) >> 7; + +		crc = ((crc & 0x7f) << 1) ^ (bit ? 0x1d : 0); + +		octet = octet << 1; +	} + +	return crc; +} + +/* + * Code straight from the spec to calculate the bits array + * Takes a pointer to the frame in question, a pointer to the bits array and + * the sampling frequency (as 2 bit integer) + */ +static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8]) +{ +	uint8_t sf = frame->frequency; + +	if (frame->mode == MONO || frame->mode == DUAL_CHANNEL) { +		int bitneed[2][8], loudness, max_bitneed, bitcount, slicecount, bitslice; +		int ch, sb; + +		for (ch = 0; ch < frame->channels; ch++) { +			max_bitneed = 0; +			if (frame->allocation == SNR) { +				for (sb = 0; sb < frame->subbands; sb++) { +					bitneed[ch][sb] = frame->scale_factor[ch][sb]; +					if (bitneed[ch][sb] > max_bitneed) +						max_bitneed = bitneed[ch][sb]; +				} +			} else { +				for (sb = 0; sb < frame->subbands; sb++) { +					if (frame->scale_factor[ch][sb] == 0) +						bitneed[ch][sb] = -5; +					else { +						if (frame->subbands == 4) +							loudness = frame->scale_factor[ch][sb] - sbc_offset4[sf][sb]; +						else +							loudness = frame->scale_factor[ch][sb] - sbc_offset8[sf][sb]; +						if (loudness > 0) +							bitneed[ch][sb] = loudness / 2; +						else +							bitneed[ch][sb] = loudness; +					} +					if (bitneed[ch][sb] > max_bitneed) +						max_bitneed = bitneed[ch][sb]; +				} +			} + +			bitcount = 0; +			slicecount = 0; +			bitslice = max_bitneed + 1; +			do { +				bitslice--; +				bitcount += slicecount; +				slicecount = 0; +				for (sb = 0; sb < frame->subbands; sb++) { +					if ((bitneed[ch][sb] > bitslice + 1) && (bitneed[ch][sb] < bitslice + 16)) +						slicecount++; +					else if (bitneed[ch][sb] == bitslice + 1) +						slicecount += 2; +				} +			} while (bitcount + slicecount < frame->bitpool); + +			if (bitcount + slicecount == frame->bitpool) { +				bitcount += slicecount; +				bitslice--; +			} + +			for (sb = 0; sb < frame->subbands; sb++) { +				if (bitneed[ch][sb] < bitslice + 2) +					bits[ch][sb] = 0; +				else { +					bits[ch][sb] = bitneed[ch][sb] - bitslice; +					if (bits[ch][sb] > 16) +						bits[ch][sb] = 16; +				} +			} + +			for (sb = 0; bitcount < frame->bitpool && sb < frame->subbands; sb++) { +				if ((bits[ch][sb] >= 2) && (bits[ch][sb] < 16)) { +					bits[ch][sb]++; +					bitcount++; +				} else if ((bitneed[ch][sb] == bitslice + 1) && (frame->bitpool > bitcount + 1)) { +					bits[ch][sb] = 2; +					bitcount += 2; +				} +			} + +			for (sb = 0; bitcount < frame->bitpool && sb < frame->subbands; sb++) { +				if (bits[ch][sb] < 16) { +					bits[ch][sb]++; +					bitcount++; +				} +			} + +		} + +	} else if (frame->mode == STEREO || frame->mode == JOINT_STEREO) { +		int bitneed[2][8], loudness, max_bitneed, bitcount, slicecount, bitslice; +		int ch, sb; + +		max_bitneed = 0; +		if (frame->allocation == SNR) { +			for (ch = 0; ch < 2; ch++) { +				for (sb = 0; sb < frame->subbands; sb++) { +					bitneed[ch][sb] = frame->scale_factor[ch][sb]; +					if (bitneed[ch][sb] > max_bitneed) +						max_bitneed = bitneed[ch][sb]; +				} +			} +		} else { +			for (ch = 0; ch < 2; ch++) { +				for (sb = 0; sb < frame->subbands; sb++) { +					if (frame->scale_factor[ch][sb] == 0) +						bitneed[ch][sb] = -5; +					else { +						if (frame->subbands == 4) +							loudness = frame->scale_factor[ch][sb] - sbc_offset4[sf][sb]; +						else +							loudness = frame->scale_factor[ch][sb] - sbc_offset8[sf][sb]; +						if (loudness > 0) +							bitneed[ch][sb] = loudness / 2; +						else +							bitneed[ch][sb] = loudness; +					} +					if (bitneed[ch][sb] > max_bitneed) +						max_bitneed = bitneed[ch][sb]; +				} +			} +		} + +		bitcount = 0; +		slicecount = 0; +		bitslice = max_bitneed + 1; +		do { +			bitslice--; +			bitcount += slicecount; +			slicecount = 0; +			for (ch = 0; ch < 2; ch++) { +				for (sb = 0; sb < frame->subbands; sb++) { +					if ((bitneed[ch][sb] > bitslice + 1) && (bitneed[ch][sb] < bitslice + 16)) +						slicecount++; +					else if (bitneed[ch][sb] == bitslice + 1) +						slicecount += 2; +				} +			} +		} while (bitcount + slicecount < frame->bitpool); + +		if (bitcount + slicecount == frame->bitpool) { +			bitcount += slicecount; +			bitslice--; +		} + +		for (ch = 0; ch < 2; ch++) { +			for (sb = 0; sb < frame->subbands; sb++) { +				if (bitneed[ch][sb] < bitslice + 2) { +					bits[ch][sb] = 0; +				} else { +					bits[ch][sb] = bitneed[ch][sb] - bitslice; +					if (bits[ch][sb] > 16) +						bits[ch][sb] = 16; +				} +			} +		} + +		ch = 0; +		sb = 0; +		while (bitcount < frame->bitpool) { +			if ((bits[ch][sb] >= 2) && (bits[ch][sb] < 16)) { +				bits[ch][sb]++; +				bitcount++; +			} else if ((bitneed[ch][sb] == bitslice + 1) && (frame->bitpool > bitcount + 1)) { +				bits[ch][sb] = 2; +				bitcount += 2; +			} +			if (ch == 1) { +				ch = 0; +				sb++; +				if (sb >= frame->subbands) break; +			} else +				ch = 1; +		} + +		ch = 0; +		sb = 0; +		while (bitcount < frame->bitpool) { +			if (bits[ch][sb] < 16) { +				bits[ch][sb]++; +				bitcount++; +			} +			if (ch == 1) { +				ch = 0; +				sb++; +				if (sb >= frame->subbands) break; +			} else +				ch = 1; +		} + +	} + +} + +/* + * Unpacks a SBC frame at the beginning of the stream in data, + * which has at most len bytes into frame. + * Returns the length in bytes of the packed frame, or a negative + * value on error. The error codes are: + * + *  -1   Data stream too short + *  -2   Sync byte incorrect + *  -3   CRC8 incorrect + *  -4   Bitpool value out of bounds + */ +static int sbc_unpack_frame(const uint8_t *data, struct sbc_frame *frame, +				size_t len) +{ +	int consumed; +	/* Will copy the parts of the header that are relevant to crc +	 * calculation here */ +	uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +	int crc_pos = 0; +	int32_t temp; + +	int audio_sample; +	int ch, sb, blk, bit;	/* channel, subband, block and bit standard +				   counters */ +	int bits[2][8];		/* bits distribution */ +	uint32_t levels[2][8];	/* levels derived from that */ + +	if (len < 4) +		return -1; + +	if (data[0] != SBC_SYNCWORD) +		return -2; + +	frame->frequency = (data[1] >> 6) & 0x03; + +	frame->block_mode = (data[1] >> 4) & 0x03; +	switch (frame->block_mode) { +	case SBC_BLK_4: +		frame->blocks = 4; +		break; +	case SBC_BLK_8: +		frame->blocks = 8; +		break; +	case SBC_BLK_12: +		frame->blocks = 12; +		break; +	case SBC_BLK_16: +		frame->blocks = 16; +		break; +	} + +	frame->mode = (data[1] >> 2) & 0x03; +	switch (frame->mode) { +	case MONO: +		frame->channels = 1; +		break; +	case DUAL_CHANNEL:	/* fall-through */ +	case STEREO: +	case JOINT_STEREO: +		frame->channels = 2; +		break; +	} + +	frame->allocation = (data[1] >> 1) & 0x01; + +	frame->subband_mode = (data[1] & 0x01); +	frame->subbands = frame->subband_mode ? 8 : 4; + +	frame->bitpool = data[2]; + +	if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) && +			frame->bitpool > 16 * frame->subbands) +		return -4; + +	if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) && +			frame->bitpool > 32 * frame->subbands) +		return -4; + +	/* data[3] is crc, we're checking it later */ + +	consumed = 32; + +	crc_header[0] = data[1]; +	crc_header[1] = data[2]; +	crc_pos = 16; + +	if (frame->mode == JOINT_STEREO) { +		if (len * 8 < consumed + frame->subbands) +			return -1; + +		frame->joint = 0x00; +		for (sb = 0; sb < frame->subbands - 1; sb++) +			frame->joint |= ((data[4] >> (7 - sb)) & 0x01) << sb; +		if (frame->subbands == 4) +			crc_header[crc_pos / 8] = data[4] & 0xf0; +		else +			crc_header[crc_pos / 8] = data[4]; + +		consumed += frame->subbands; +		crc_pos += frame->subbands; +	} + +	if (len * 8 < consumed + (4 * frame->subbands * frame->channels)) +		return -1; + +	for (ch = 0; ch < frame->channels; ch++) { +		for (sb = 0; sb < frame->subbands; sb++) { +			/* FIXME assert(consumed % 4 == 0); */ +			frame->scale_factor[ch][sb] = +				(data[consumed >> 3] >> (4 - (consumed & 0x7))) & 0x0F; +			crc_header[crc_pos >> 3] |= +				frame->scale_factor[ch][sb] << (4 - (crc_pos & 0x7)); + +			consumed += 4; +			crc_pos += 4; +		} +	} + +	if (data[3] != sbc_crc8(crc_header, crc_pos)) +		return -3; + +	sbc_calculate_bits(frame, bits); + +	for (ch = 0; ch < frame->channels; ch++) { +		for (sb = 0; sb < frame->subbands; sb++) +			levels[ch][sb] = (1 << bits[ch][sb]) - 1; +	} + +	for (blk = 0; blk < frame->blocks; blk++) { +		for (ch = 0; ch < frame->channels; ch++) { +			for (sb = 0; sb < frame->subbands; sb++) { +				if (levels[ch][sb] > 0) { +					audio_sample = 0; +					for (bit = 0; bit < bits[ch][sb]; bit++) { +						if (consumed > len * 8) +							return -1; + +						if ((data[consumed >> 3] >> (7 - (consumed & 0x7))) & 0x01) +							audio_sample |= 1 << (bits[ch][sb] - bit - 1); + +						consumed++; +					} + +					frame->sb_sample[blk][ch][sb] = +						(((audio_sample << 1) | 1) << frame->scale_factor[ch][sb]) / +						levels[ch][sb] - (1 << frame->scale_factor[ch][sb]); +				} else +					frame->sb_sample[blk][ch][sb] = 0; +			} +		} +	} + +	if (frame->mode == JOINT_STEREO) { +		for (blk = 0; blk < frame->blocks; blk++) { +			for (sb = 0; sb < frame->subbands; sb++) { +				if (frame->joint & (0x01 << sb)) { +					temp = frame->sb_sample[blk][0][sb] + +						frame->sb_sample[blk][1][sb]; +					frame->sb_sample[blk][1][sb] = +						frame->sb_sample[blk][0][sb] - +						frame->sb_sample[blk][1][sb]; +					frame->sb_sample[blk][0][sb] = temp; +				} +			} +		} +	} + +	if ((consumed & 0x7) != 0) +		consumed += 8 - (consumed & 0x7); + +	return consumed >> 3; +} + +static void sbc_decoder_init(struct sbc_decoder_state *state, +				const struct sbc_frame *frame) +{ +	int i, ch; + +	memset(state->V, 0, sizeof(state->V)); +	state->subbands = frame->subbands; + +	for (ch = 0; ch < 2; ch++) +		for (i = 0; i < frame->subbands * 2; i++) +			state->offset[ch][i] = (10 * i + 10); +} + +static inline void sbc_synthesize_four(struct sbc_decoder_state *state, +				struct sbc_frame *frame, int ch, int blk) +{ +	int i, k, idx; +	int32_t *v = state->V[ch]; +	int *offset = state->offset[ch]; + +	for (i = 0; i < 8; i++) { +		/* Shifting */ +		offset[i]--; +		if (offset[i] < 0) { +			offset[i] = 79; +			memcpy(v + 80, v, 9 * sizeof(*v)); +		} + +		/* Distribute the new matrix value to the shifted position */ +		v[offset[i]] = SCALE4_STAGED1( +			MULA(synmatrix4[i][0], frame->sb_sample[blk][ch][0], +			MULA(synmatrix4[i][1], frame->sb_sample[blk][ch][1], +			MULA(synmatrix4[i][2], frame->sb_sample[blk][ch][2], +			MUL (synmatrix4[i][3], frame->sb_sample[blk][ch][3]))))); +	} + +	/* Compute the samples */ +	for (idx = 0, i = 0; i < 4; i++, idx += 5) { +		k = (i + 4) & 0xf; + +		/* Store in output, Q0 */ +		frame->pcm_sample[ch][blk * 4 + i] = SCALE4_STAGED2( +			MULA(v[offset[i] + 0], sbc_proto_4_40m0[idx + 0], +			MULA(v[offset[k] + 1], sbc_proto_4_40m1[idx + 0], +			MULA(v[offset[i] + 2], sbc_proto_4_40m0[idx + 1], +			MULA(v[offset[k] + 3], sbc_proto_4_40m1[idx + 1], +			MULA(v[offset[i] + 4], sbc_proto_4_40m0[idx + 2], +			MULA(v[offset[k] + 5], sbc_proto_4_40m1[idx + 2], +			MULA(v[offset[i] + 6], sbc_proto_4_40m0[idx + 3], +			MULA(v[offset[k] + 7], sbc_proto_4_40m1[idx + 3], +			MULA(v[offset[i] + 8], sbc_proto_4_40m0[idx + 4], +			MUL( v[offset[k] + 9], sbc_proto_4_40m1[idx + 4]))))))))))); +	} +} + +static inline void sbc_synthesize_eight(struct sbc_decoder_state *state, +				struct sbc_frame *frame, int ch, int blk) +{ +	int i, j, k, idx; +	int *offset = state->offset[ch]; + +	for (i = 0; i < 16; i++) { +		/* Shifting */ +		offset[i]--; +		if (offset[i] < 0) { +			offset[i] = 159; +			for (j = 0; j < 9; j++) +				state->V[ch][j + 160] = state->V[ch][j]; +		} + +		/* Distribute the new matrix value to the shifted position */ +		state->V[ch][offset[i]] = SCALE8_STAGED1( +			MULA(synmatrix8[i][0], frame->sb_sample[blk][ch][0], +			MULA(synmatrix8[i][1], frame->sb_sample[blk][ch][1], +			MULA(synmatrix8[i][2], frame->sb_sample[blk][ch][2], +			MULA(synmatrix8[i][3], frame->sb_sample[blk][ch][3], +			MULA(synmatrix8[i][4], frame->sb_sample[blk][ch][4], +			MULA(synmatrix8[i][5], frame->sb_sample[blk][ch][5], +			MULA(synmatrix8[i][6], frame->sb_sample[blk][ch][6], +			MUL( synmatrix8[i][7], frame->sb_sample[blk][ch][7]))))))))); +	} + +	/* Compute the samples */ +	for (idx = 0, i = 0; i < 8; i++, idx += 5) { +		k = (i + 8) & 0xf; + +		/* Store in output */ +		frame->pcm_sample[ch][blk * 8 + i] = SCALE8_STAGED2( // Q0 +			MULA(state->V[ch][offset[i] + 0], sbc_proto_8_80m0[idx + 0], +			MULA(state->V[ch][offset[k] + 1], sbc_proto_8_80m1[idx + 0], +			MULA(state->V[ch][offset[i] + 2], sbc_proto_8_80m0[idx + 1], +			MULA(state->V[ch][offset[k] + 3], sbc_proto_8_80m1[idx + 1], +			MULA(state->V[ch][offset[i] + 4], sbc_proto_8_80m0[idx + 2], +			MULA(state->V[ch][offset[k] + 5], sbc_proto_8_80m1[idx + 2], +			MULA(state->V[ch][offset[i] + 6], sbc_proto_8_80m0[idx + 3], +			MULA(state->V[ch][offset[k] + 7], sbc_proto_8_80m1[idx + 3], +			MULA(state->V[ch][offset[i] + 8], sbc_proto_8_80m0[idx + 4], +			MUL( state->V[ch][offset[k] + 9], sbc_proto_8_80m1[idx + 4]))))))))))); +	} +} + +static int sbc_synthesize_audio(struct sbc_decoder_state *state, +				struct sbc_frame *frame) +{ +	int ch, blk; + +	switch (frame->subbands) { +	case 4: +		for (ch = 0; ch < frame->channels; ch++) { +			for (blk = 0; blk < frame->blocks; blk++) +				sbc_synthesize_four(state, frame, ch, blk); +		} +		return frame->blocks * 4; + +	case 8: +		for (ch = 0; ch < frame->channels; ch++) { +			for (blk = 0; blk < frame->blocks; blk++) +				sbc_synthesize_eight(state, frame, ch, blk); +		} +		return frame->blocks * 8; + +	default: +		return -EIO; +	} +} + +static void sbc_encoder_init(struct sbc_encoder_state *state, +				const struct sbc_frame *frame) +{ +	memset(&state->X, 0, sizeof(state->X)); +	state->subbands = frame->subbands; +	state->position[0] = state->position[1] = 9 * frame->subbands; +} + +static inline void _sbc_analyze_four(const int32_t *in, int32_t *out) +{ +	sbc_fixed_t t[8], s[5]; + +	t[0] = SCALE4_STAGE1( /* Q8 */ +		MULA(_sbc_proto_4[0], in[8] - in[32], /* Q18 */ +		MUL( _sbc_proto_4[1], in[16] - in[24]))); + +	t[1] = SCALE4_STAGE1( +		MULA(_sbc_proto_4[2], in[1], +		MULA(_sbc_proto_4[3], in[9], +		MULA(_sbc_proto_4[4], in[17], +		MULA(_sbc_proto_4[5], in[25], +		MUL( _sbc_proto_4[6], in[33])))))); + +	t[2] = SCALE4_STAGE1( +		MULA(_sbc_proto_4[7], in[2], +		MULA(_sbc_proto_4[8], in[10], +		MULA(_sbc_proto_4[9], in[18], +		MULA(_sbc_proto_4[10], in[26], +		MUL( _sbc_proto_4[11], in[34])))))); + +	t[3] = SCALE4_STAGE1( +		MULA(_sbc_proto_4[12], in[3], +		MULA(_sbc_proto_4[13], in[11], +		MULA(_sbc_proto_4[14], in[19], +		MULA(_sbc_proto_4[15], in[27], +		MUL( _sbc_proto_4[16], in[35])))))); + +	t[4] = SCALE4_STAGE1( +		MULA(_sbc_proto_4[17], in[4] + in[36], +		MULA(_sbc_proto_4[18], in[12] + in[28], +		MUL( _sbc_proto_4[19], in[20])))); + +	t[5] = SCALE4_STAGE1( +		MULA(_sbc_proto_4[16], in[5], +		MULA(_sbc_proto_4[15], in[13], +		MULA(_sbc_proto_4[14], in[21], +		MULA(_sbc_proto_4[13], in[29], +		MUL( _sbc_proto_4[12], in[37])))))); + +	/* don't compute t[6]... this term always multiplies +	 * with cos(pi/2) = 0 */ + +	t[7] = SCALE4_STAGE1( +		MULA(_sbc_proto_4[6], in[7], +		MULA(_sbc_proto_4[5], in[15], +		MULA(_sbc_proto_4[4], in[23], +		MULA(_sbc_proto_4[3], in[31], +		MUL( _sbc_proto_4[2], in[39])))))); + +	s[0] = MUL( _anamatrix4[0], t[0] + t[4]); +	s[1] = MUL( _anamatrix4[2], t[2]); +	s[2] = MULA(_anamatrix4[1], t[1] + t[3], +		MUL(_anamatrix4[3], t[5])); +	s[3] = MULA(_anamatrix4[3], t[1] + t[3], +		MUL(_anamatrix4[1], -t[5] + t[7])); +	s[4] = MUL( _anamatrix4[3], t[7]); + +	out[0] = SCALE4_STAGE2( s[0] + s[1] + s[2] + s[4]); /* Q0 */ +	out[1] = SCALE4_STAGE2(-s[0] + s[1] + s[3]); +	out[2] = SCALE4_STAGE2(-s[0] + s[1] - s[3]); +	out[3] = SCALE4_STAGE2( s[0] + s[1] - s[2] - s[4]); +} + +static inline void sbc_analyze_four(struct sbc_encoder_state *state, +				struct sbc_frame *frame, int ch, int blk) +{ +	int32_t *x = &state->X[ch][state->position[ch]]; +	int16_t *pcm = &frame->pcm_sample[ch][blk * 4]; + +	/* Input 4 Audio Samples */ +	x[40] = x[0] = pcm[3]; +	x[41] = x[1] = pcm[2]; +	x[42] = x[2] = pcm[1]; +	x[43] = x[3] = pcm[0]; + +	_sbc_analyze_four(x, frame->sb_sample_f[blk][ch]); + +	state->position[ch] -= 4; +	if (state->position[ch] < 0) +		state->position[ch] = 36; +} + +static inline void _sbc_analyze_eight(const int32_t *in, int32_t *out) +{ +	sbc_fixed_t t[8], s[8]; + +	t[0] = SCALE8_STAGE1( /* Q10 */ +		MULA(_sbc_proto_8[0], (in[16] - in[64]), /* Q18 = Q18 * Q0 */ +		MULA(_sbc_proto_8[1], (in[32] - in[48]), +		MULA(_sbc_proto_8[2], in[4], +		MULA(_sbc_proto_8[3], in[20], +		MULA(_sbc_proto_8[4], in[36], +		MUL( _sbc_proto_8[5], in[52]))))))); + +	t[1] = SCALE8_STAGE1( +		MULA(_sbc_proto_8[6], in[2], +		MULA(_sbc_proto_8[7], in[18], +		MULA(_sbc_proto_8[8], in[34], +		MULA(_sbc_proto_8[9], in[50], +		MUL(_sbc_proto_8[10], in[66])))))); + +	t[2] = SCALE8_STAGE1( +		MULA(_sbc_proto_8[11], in[1], +		MULA(_sbc_proto_8[12], in[17], +		MULA(_sbc_proto_8[13], in[33], +		MULA(_sbc_proto_8[14], in[49], +		MULA(_sbc_proto_8[15], in[65], +		MULA(_sbc_proto_8[16], in[3], +		MULA(_sbc_proto_8[17], in[19], +		MULA(_sbc_proto_8[18], in[35], +		MULA(_sbc_proto_8[19], in[51], +		MUL( _sbc_proto_8[20], in[67]))))))))))); + +	t[3] = SCALE8_STAGE1( +		MULA( _sbc_proto_8[21], in[5], +		MULA( _sbc_proto_8[22], in[21], +		MULA( _sbc_proto_8[23], in[37], +		MULA( _sbc_proto_8[24], in[53], +		MULA( _sbc_proto_8[25], in[69], +		MULA(-_sbc_proto_8[15], in[15], +		MULA(-_sbc_proto_8[14], in[31], +		MULA(-_sbc_proto_8[13], in[47], +		MULA(-_sbc_proto_8[12], in[63], +		MUL( -_sbc_proto_8[11], in[79]))))))))))); + +	t[4] = SCALE8_STAGE1( +		MULA( _sbc_proto_8[26], in[6], +		MULA( _sbc_proto_8[27], in[22], +		MULA( _sbc_proto_8[28], in[38], +		MULA( _sbc_proto_8[29], in[54], +		MULA( _sbc_proto_8[30], in[70], +		MULA(-_sbc_proto_8[10], in[14], +		MULA(-_sbc_proto_8[9], in[30], +		MULA(-_sbc_proto_8[8], in[46], +		MULA(-_sbc_proto_8[7], in[62], +		MUL( -_sbc_proto_8[6], in[78]))))))))))); + +	t[5] = SCALE8_STAGE1( +		MULA( _sbc_proto_8[31], in[7], +		MULA( _sbc_proto_8[32], in[23], +		MULA( _sbc_proto_8[33], in[39], +		MULA( _sbc_proto_8[34], in[55], +		MULA( _sbc_proto_8[35], in[71], +		MULA(-_sbc_proto_8[20], in[13], +		MULA(-_sbc_proto_8[19], in[29], +		MULA(-_sbc_proto_8[18], in[45], +		MULA(-_sbc_proto_8[17], in[61], +		MUL( -_sbc_proto_8[16], in[77]))))))))))); + +	t[6] = SCALE8_STAGE1( +		MULA( _sbc_proto_8[36], (in[8] + in[72]), +		MULA( _sbc_proto_8[37], (in[24] + in[56]), +		MULA( _sbc_proto_8[38], in[40], +		MULA(-_sbc_proto_8[39], in[12], +		MULA(-_sbc_proto_8[5], in[28], +		MULA(-_sbc_proto_8[4], in[44], +		MULA(-_sbc_proto_8[3], in[60], +		MUL( -_sbc_proto_8[2], in[76]))))))))); + +	t[7] = SCALE8_STAGE1( +		MULA( _sbc_proto_8[35], in[9], +		MULA( _sbc_proto_8[34], in[25], +		MULA( _sbc_proto_8[33], in[41], +		MULA( _sbc_proto_8[32], in[57], +		MULA( _sbc_proto_8[31], in[73], +		MULA(-_sbc_proto_8[25], in[11], +		MULA(-_sbc_proto_8[24], in[27], +		MULA(-_sbc_proto_8[23], in[43], +		MULA(-_sbc_proto_8[22], in[59], +		MUL( -_sbc_proto_8[21], in[75]))))))))))); + +	s[0] = MULA(  _anamatrix8[0], t[0], +		MUL(  _anamatrix8[1], t[6])); +	s[1] = MUL(   _anamatrix8[7], t[1]); +	s[2] = MULA(  _anamatrix8[2], t[2], +		MULA( _anamatrix8[3], t[3], +		MULA( _anamatrix8[4], t[5], +		MUL(  _anamatrix8[5], t[7])))); +	s[3] = MUL(   _anamatrix8[6], t[4]); +	s[4] = MULA(  _anamatrix8[3], t[2], +		MULA(-_anamatrix8[5], t[3], +		MULA(-_anamatrix8[2], t[5], +		MUL( -_anamatrix8[4], t[7])))); +	s[5] = MULA(  _anamatrix8[4], t[2], +		MULA(-_anamatrix8[2], t[3], +		MULA( _anamatrix8[5], t[5], +		MUL(  _anamatrix8[3], t[7])))); +	s[6] = MULA(  _anamatrix8[1], t[0], +		MUL( -_anamatrix8[0], t[6])); +	s[7] = MULA(  _anamatrix8[5], t[2], +		MULA(-_anamatrix8[4], t[3], +		MULA( _anamatrix8[3], t[5], +		MUL( -_anamatrix8[2], t[7])))); + +	out[0] = SCALE8_STAGE2( s[0] + s[1] + s[2] + s[3]); +	out[1] = SCALE8_STAGE2( s[1] - s[3] + s[4] + s[6]); +	out[2] = SCALE8_STAGE2( s[1] - s[3] + s[5] - s[6]); +	out[3] = SCALE8_STAGE2(-s[0] + s[1] + s[3] + s[7]); +	out[4] = SCALE8_STAGE2(-s[0] + s[1] + s[3] - s[7]); +	out[5] = SCALE8_STAGE2( s[1] - s[3] - s[5] - s[6]); +	out[6] = SCALE8_STAGE2( s[1] - s[3] - s[4] + s[6]); +	out[7] = SCALE8_STAGE2( s[0] + s[1] - s[2] + s[3]); +} + +static inline void sbc_analyze_eight(struct sbc_encoder_state *state, +					struct sbc_frame *frame, int ch, +					int blk) +{ +	int32_t *x = &state->X[ch][state->position[ch]]; +	int16_t *pcm = &frame->pcm_sample[ch][blk * 8]; + +	/* Input 8 Audio Samples */ +	x[80] = x[0] = pcm[7]; +	x[81] = x[1] = pcm[6]; +	x[82] = x[2] = pcm[5]; +	x[83] = x[3] = pcm[4]; +	x[84] = x[4] = pcm[3]; +	x[85] = x[5] = pcm[2]; +	x[86] = x[6] = pcm[1]; +	x[87] = x[7] = pcm[0]; + +	_sbc_analyze_eight(x, frame->sb_sample_f[blk][ch]); + +	state->position[ch] -= 8; +	if (state->position[ch] < 0) +		state->position[ch] = 72; +} + +static int sbc_analyze_audio(struct sbc_encoder_state *state, +				struct sbc_frame *frame) +{ +	int ch, blk; + +	switch (frame->subbands) { +	case 4: +		for (ch = 0; ch < frame->channels; ch++) +			for (blk = 0; blk < frame->blocks; blk++) +				sbc_analyze_four(state, frame, ch, blk); +		return frame->blocks * 4; + +	case 8: +		for (ch = 0; ch < frame->channels; ch++) +			for (blk = 0; blk < frame->blocks; blk++) +				sbc_analyze_eight(state, frame, ch, blk); +		return frame->blocks * 8; + +	default: +		return -EIO; +	} +} + +/* + * Packs the SBC frame from frame into the memory at data. At most len + * bytes will be used, should more memory be needed an appropriate + * error code will be returned. Returns the length of the packed frame + * on success or a negative value on error. + * + * The error codes are: + * -1 Not enough memory reserved + * -2 Unsupported sampling rate + * -3 Unsupported number of blocks + * -4 Unsupported number of subbands + * -5 Bitpool value out of bounds + * -99 not implemented + */ + +static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len) +{ +	int produced; +	/* Will copy the header parts for CRC-8 calculation here */ +	uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +	int crc_pos = 0; + +	uint16_t audio_sample; + +	int ch, sb, blk, bit;	/* channel, subband, block and bit counters */ +	int bits[2][8];		/* bits distribution */ +	int levels[2][8];	/* levels are derived from that */ + +	u_int32_t scalefactor[2][8];	/* derived from frame->scale_factor */ + +	data[0] = SBC_SYNCWORD; + +	data[1] = (frame->frequency & 0x03) << 6; + +	data[1] |= (frame->block_mode & 0x03) << 4; + +	data[1] |= (frame->mode & 0x03) << 2; + +	data[1] |= (frame->allocation & 0x01) << 1; + +	switch (frame->subbands) { +	case 4: +		/* Nothing to do */ +		break; +	case 8: +		data[1] |= 0x01; +		break; +	default: +		return -4; +		break; +	} + +	data[2] = frame->bitpool; + +	if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) && +			frame->bitpool > frame->subbands << 4) +		return -5; + +	if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) && +			frame->bitpool > frame->subbands << 5) +		return -5; + +	/* Can't fill in crc yet */ + +	produced = 32; + +	crc_header[0] = data[1]; +	crc_header[1] = data[2]; +	crc_pos = 16; + +	for (ch = 0; ch < frame->channels; ch++) { +		for (sb = 0; sb < frame->subbands; sb++) { +			frame->scale_factor[ch][sb] = 0; +			scalefactor[ch][sb] = 2; +			for (blk = 0; blk < frame->blocks; blk++) { +				while (scalefactor[ch][sb] < fabs(frame->sb_sample_f[blk][ch][sb])) { +					frame->scale_factor[ch][sb]++; +					scalefactor[ch][sb] *= 2; +				} +			} +		} +	} + +	if (frame->mode == JOINT_STEREO) { +		/* like frame->sb_sample but joint stereo */ +		int32_t sb_sample_j[16][2]; +		/* scalefactor and scale_factor in joint case */ +		u_int32_t scalefactor_j[2]; +		uint8_t scale_factor_j[2]; + +		frame->joint = 0; + +		for (sb = 0; sb < frame->subbands - 1; sb++) { +			scale_factor_j[0] = 0; +			scalefactor_j[0] = 2; +			scale_factor_j[1] = 0; +			scalefactor_j[1] = 2; + +			for (blk = 0; blk < frame->blocks; blk++) { +				/* Calculate joint stereo signal */ +				sb_sample_j[blk][0] = +					(frame->sb_sample_f[blk][0][sb] + +						frame->sb_sample_f[blk][1][sb]) >> 1; +				sb_sample_j[blk][1] = +					(frame->sb_sample_f[blk][0][sb] - +						frame->sb_sample_f[blk][1][sb]) >> 1; + +				/* calculate scale_factor_j and scalefactor_j for joint case */ +				while (scalefactor_j[0] < fabs(sb_sample_j[blk][0])) { +					scale_factor_j[0]++; +					scalefactor_j[0] *= 2; +				} +				while (scalefactor_j[1] < fabs(sb_sample_j[blk][1])) { +					scale_factor_j[1]++; +					scalefactor_j[1] *= 2; +				} +			} + +			/* decide whether to join this subband */ +			if ((scalefactor[0][sb] + scalefactor[1][sb]) > +					(scalefactor_j[0] + scalefactor_j[1]) ) { +				/* use joint stereo for this subband */ +				frame->joint |= 1 << sb; +				frame->scale_factor[0][sb] = scale_factor_j[0]; +				frame->scale_factor[1][sb] = scale_factor_j[1]; +				scalefactor[0][sb] = scalefactor_j[0]; +				scalefactor[1][sb] = scalefactor_j[1]; +				for (blk = 0; blk < frame->blocks; blk++) { +					frame->sb_sample_f[blk][0][sb] = +							sb_sample_j[blk][0]; +					frame->sb_sample_f[blk][1][sb] = +							sb_sample_j[blk][1]; +				} +			} +		} + +		data[4] = 0; +		for (sb = 0; sb < frame->subbands - 1; sb++) +			data[4] |= ((frame->joint >> sb) & 0x01) << (frame->subbands - 1 - sb); + +		crc_header[crc_pos >> 3] = data[4]; + +		produced += frame->subbands; +		crc_pos += frame->subbands; +	} + +	for (ch = 0; ch < frame->channels; ch++) { +		for (sb = 0; sb < frame->subbands; sb++) { +			data[produced >> 3] <<= 4; +			crc_header[crc_pos >> 3] <<= 4; +			data[produced >> 3] |= frame->scale_factor[ch][sb] & 0x0F; +			crc_header[crc_pos >> 3] |= frame->scale_factor[ch][sb] & 0x0F; + +			produced += 4; +			crc_pos += 4; +		} +	} + +	/* align the last crc byte */ +	if (crc_pos % 8) +		crc_header[crc_pos >> 3] <<= 8 - (crc_pos % 8); + +	data[3] = sbc_crc8(crc_header, crc_pos); + +	sbc_calculate_bits(frame, bits); + +	for (ch = 0; ch < frame->channels; ch++) { +		for (sb = 0; sb < frame->subbands; sb++) +			levels[ch][sb] = (1 << bits[ch][sb]) - 1; +	} + +	for (blk = 0; blk < frame->blocks; blk++) { +		for (ch = 0; ch < frame->channels; ch++) { +			for (sb = 0; sb < frame->subbands; sb++) { +				if (levels[ch][sb] > 0) { +					audio_sample = +						(uint16_t) ((((frame->sb_sample_f[blk][ch][sb]*levels[ch][sb]) >> +									(frame->scale_factor[ch][sb] + 1)) + +								levels[ch][sb]) >> 1); +					audio_sample <<= 16 - bits[ch][sb]; +					for (bit = 0; bit < bits[ch][sb]; bit++) { +						data[produced >> 3] <<= 1; +						if (audio_sample & 0x8000) +							data[produced >> 3] |= 0x1; +						audio_sample <<= 1; +						produced++; +					} +				} +			} +		} +	} + +	/* align the last byte */ +	if (produced % 8) { +		data[produced >> 3] <<= 8 - (produced % 8); +	} + +	return (produced + 7) >> 3; +} + +struct sbc_priv { +	int init; +	struct sbc_frame frame; +	struct sbc_decoder_state dec_state; +	struct sbc_encoder_state enc_state; +}; + +static void sbc_set_defaults(sbc_t *sbc, unsigned long flags) +{ +	sbc->frequency = SBC_FREQ_44100; +	sbc->mode = SBC_MODE_STEREO; +	sbc->subbands = SBC_SB_8; +	sbc->blocks = SBC_BLK_16; +	sbc->bitpool = 32; +#if __BYTE_ORDER == __LITTLE_ENDIAN +	sbc->endian = SBC_LE; +#elif __BYTE_ORDER == __BIG_ENDIAN +	sbc->endian = SBC_BE; +#else +#error "Unknown byte order" +#endif +} + +int sbc_init(sbc_t *sbc, unsigned long flags) +{ +	if (!sbc) +		return -EIO; + +	memset(sbc, 0, sizeof(sbc_t)); + +	sbc->priv = malloc(sizeof(struct sbc_priv)); +	if (!sbc->priv) +		return -ENOMEM; + +	memset(sbc->priv, 0, sizeof(struct sbc_priv)); + +	sbc_set_defaults(sbc, flags); + +	return 0; +} + +int sbc_parse(sbc_t *sbc, void *input, int input_len) +{ +	return sbc_decode(sbc, input, input_len, NULL, 0, NULL); +} + +int sbc_decode(sbc_t *sbc, void *input, int input_len, void *output, +		int output_len, int *written) +{ +	struct sbc_priv *priv; +	char *ptr; +	int i, ch, framelen, samples; + +	if (!sbc && !input) +		return -EIO; + +	priv = sbc->priv; + +	framelen = sbc_unpack_frame(input, &priv->frame, input_len); + +	if (!priv->init) { +		sbc_decoder_init(&priv->dec_state, &priv->frame); +		priv->init = 1; + +		sbc->frequency = priv->frame.frequency; +		sbc->mode = priv->frame.mode; +		sbc->subbands = priv->frame.subband_mode; +		sbc->blocks = priv->frame.block_mode; +		sbc->allocation = priv->frame.allocation; +		sbc->bitpool = priv->frame.bitpool; + +		priv->frame.codesize = sbc_get_codesize(sbc); +		priv->frame.length = sbc_get_frame_length(sbc); +	} + +	if (!output) +		return framelen; + +	if (written) +		*written = 0; + +	samples = sbc_synthesize_audio(&priv->dec_state, &priv->frame); + +	ptr = output; + +	if (output_len < samples * priv->frame.channels * 2) +		samples = output_len / (priv->frame.channels * 2); + +	for (i = 0; i < samples; i++) { +		for (ch = 0; ch < priv->frame.channels; ch++) { +			int16_t s; +			s = priv->frame.pcm_sample[ch][i]; + +#if __BYTE_ORDER == __LITTLE_ENDIAN +			if (sbc->endian == SBC_BE) { +#elif __BYTE_ORDER == __BIG_ENDIAN +			if (sbc->endian == SBC_LE) { +#else +#error "Unknown byte order" +#endif +				*ptr++ = (s & 0xff00) >> 8; +				*ptr++ = (s & 0x00ff); +			} else { +				*ptr++ = (s & 0x00ff); +				*ptr++ = (s & 0xff00) >> 8; +			} +		} +	} + +	if (written) +		*written = samples * priv->frame.channels * 2; + +	return framelen; +} + +int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output, +		int output_len, int *written) +{ +	struct sbc_priv *priv; +	char *ptr; +	int i, ch, framelen, samples; + +	if (!sbc && !input) +		return -EIO; + +	priv = sbc->priv; + +	if (written) +		*written = 0; + +	if (!priv->init) { +		priv->frame.frequency = sbc->frequency; +		priv->frame.mode = sbc->mode; +		priv->frame.channels = sbc->mode == SBC_MODE_MONO ? 1 : 2; +		priv->frame.allocation = sbc->allocation; +		priv->frame.subband_mode = sbc->subbands; +		priv->frame.subbands = sbc->subbands ? 8 : 4; +		priv->frame.block_mode = sbc->blocks; +		priv->frame.blocks = 4 + (sbc->blocks * 4); +		priv->frame.bitpool = sbc->bitpool; +		priv->frame.codesize = sbc_get_codesize(sbc); +		priv->frame.length = sbc_get_frame_length(sbc); + +		sbc_encoder_init(&priv->enc_state, &priv->frame); +		priv->init = 1; +	} + +	/* input must be large enough to encode a complete frame */ +	if (input_len < priv->frame.codesize) +		return 0; + +	/* output must be large enough to receive the encoded frame */ +	if (!output || output_len < priv->frame.length) +		return -ENOSPC; + +	ptr = input; + +	for (i = 0; i < priv->frame.subbands * priv->frame.blocks; i++) { +		for (ch = 0; ch < priv->frame.channels; ch++) { +			int16_t s; +#if __BYTE_ORDER == __LITTLE_ENDIAN +			if (sbc->endian == SBC_BE) +#elif __BYTE_ORDER == __BIG_ENDIAN +			if (sbc->endian == SBC_LE) +#else +#error "Unknown byte order" +#endif +				s = (ptr[0] & 0xff) << 8 | (ptr[1] & 0xff); +			else +				s = (ptr[0] & 0xff) | (ptr[1] & 0xff) << 8; +			ptr += 2; +			priv->frame.pcm_sample[ch][i] = s; +		} +	} + +	samples = sbc_analyze_audio(&priv->enc_state, &priv->frame); + +	framelen = sbc_pack_frame(output, &priv->frame, output_len); + +	if (written) +		*written = framelen; + +	return samples * priv->frame.channels * 2; +} + +void sbc_finish(sbc_t *sbc) +{ +	if (!sbc) +		return; + +	if (sbc->priv) +		free(sbc->priv); + +	memset(sbc, 0, sizeof(sbc_t)); +} + +int sbc_get_frame_length(sbc_t *sbc) +{ +	int ret; +	uint8_t subbands, channels, blocks, joint; +	struct sbc_priv *priv; + +	priv = sbc->priv; +	if (!priv->init) { +		subbands = sbc->subbands ? 8 : 4; +		blocks = 4 + (sbc->blocks * 4); +		channels = sbc->mode == SBC_MODE_MONO ? 1 : 2; +		joint = sbc->mode == SBC_MODE_JOINT_STEREO ? 1 : 0; +	} else { +		subbands = priv->frame.subbands; +		blocks = priv->frame.blocks; +		channels = priv->frame.channels; +		joint = priv->frame.joint; +	} + +	ret = 4 + (4 * subbands * channels) / 8; + +	/* This term is not always evenly divide so we round it up */ +	if (channels == 1) +		ret += ((blocks * channels * sbc->bitpool) + 7) / 8; +	else +		ret += (((joint ? subbands : 0) + blocks * sbc->bitpool) + 7) +			/ 8; + +	return ret; +} + +int sbc_get_frame_duration(sbc_t *sbc) +{ +	uint8_t subbands, blocks; +	uint16_t frequency; +	struct sbc_priv *priv; + +	priv = sbc->priv; +	if (!priv->init) { +		subbands = sbc->subbands ? 8 : 4; +		blocks = 4 + (sbc->blocks * 4); +	} else { +		subbands = priv->frame.subbands; +		blocks = priv->frame.blocks; +	} + +	switch (sbc->frequency) { +	case SBC_FREQ_16000: +		frequency = 16000; +		break; + +	case SBC_FREQ_32000: +		frequency = 32000; +		break; + +	case SBC_FREQ_44100: +		frequency = 44100; +		break; + +	case SBC_FREQ_48000: +		frequency = 48000; +		break; +	default: +		return 0; +	} + +	return (1000000 * blocks * subbands) / frequency; +} + +int sbc_get_codesize(sbc_t *sbc) +{ +	uint8_t subbands, channels, blocks; +	struct sbc_priv *priv; + +	priv = sbc->priv; +	if (!priv->init) { +		subbands = sbc->subbands ? 8 : 4; +		blocks = 4 + (sbc->blocks * 4); +		channels = sbc->mode == SBC_MODE_MONO ? 1 : 2; +	} else { +		subbands = priv->frame.subbands; +		blocks = priv->frame.blocks; +		channels = priv->frame.channels; +	} + +	return subbands * blocks * channels * 2; +} + +int sbc_reinit(sbc_t *sbc, unsigned long flags) +{ +	struct sbc_priv *priv; + +	if (!sbc || !sbc->priv) +		return -EIO; + +	priv = sbc->priv; + +	if (priv->init == 1) +		memset(sbc->priv, 0, sizeof(struct sbc_priv)); + +	sbc_set_defaults(sbc, flags); + +	return 0; +} diff --git a/src/modules/bluetooth/sbc.h b/src/modules/bluetooth/sbc.h new file mode 100644 index 00000000..ab47e329 --- /dev/null +++ b/src/modules/bluetooth/sbc.h @@ -0,0 +1,97 @@ +/* + * + *  Bluetooth low-complexity, subband codec (SBC) library + * + *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org> + *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch> + *  Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com> + * + * + *  This library is free software; you can redistribute it and/or + *  modify it under the terms of the GNU Lesser General Public + *  License as published by the Free Software Foundation; either + *  version 2.1 of the License, or (at your option) any later version. + * + *  This library is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + *  Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this library; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifndef __SBC_H +#define __SBC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +/* sampling frequency */ +#define SBC_FREQ_16000		0x00 +#define SBC_FREQ_32000		0x01 +#define SBC_FREQ_44100		0x02 +#define SBC_FREQ_48000		0x03 + +/* blocks */ +#define SBC_BLK_4		0x00 +#define SBC_BLK_8		0x01 +#define SBC_BLK_12		0x02 +#define SBC_BLK_16		0x03 + +/* channel mode */ +#define SBC_MODE_MONO		0x00 +#define SBC_MODE_DUAL_CHANNEL	0x01 +#define SBC_MODE_STEREO		0x02 +#define SBC_MODE_JOINT_STEREO	0x03 + +/* allocation method */ +#define SBC_AM_LOUDNESS		0x00 +#define SBC_AM_SNR		0x01 + +/* subbands */ +#define SBC_SB_4		0x00 +#define SBC_SB_8		0x01 + +/* Data endianess */ +#define SBC_LE			0x00 +#define SBC_BE			0x01 + +struct sbc_struct { +	unsigned long flags; + +	uint8_t frequency; +	uint8_t blocks; +	uint8_t subbands; +	uint8_t mode; +	uint8_t allocation; +	uint8_t bitpool; +	uint8_t endian; + +	void *priv; +}; + +typedef struct sbc_struct sbc_t; + +int sbc_init(sbc_t *sbc, unsigned long flags); +int sbc_reinit(sbc_t *sbc, unsigned long flags); +int sbc_parse(sbc_t *sbc, void *input, int input_len); +int sbc_decode(sbc_t *sbc, void *input, int input_len, void *output, +		int output_len, int *len); +int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output, +		int output_len, int *written); +int sbc_get_frame_length(sbc_t *sbc); +int sbc_get_frame_duration(sbc_t *sbc); +int sbc_get_codesize(sbc_t *sbc); +void sbc_finish(sbc_t *sbc); + +#ifdef __cplusplus +} +#endif + +#endif /* __SBC_H */ diff --git a/src/modules/bluetooth/sbc_math.h b/src/modules/bluetooth/sbc_math.h new file mode 100644 index 00000000..b3d87a62 --- /dev/null +++ b/src/modules/bluetooth/sbc_math.h @@ -0,0 +1,72 @@ +/* + * + *  Bluetooth low-complexity, subband codec (SBC) library + * + *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org> + *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch> + *  Copyright (C) 2005-2008  Brad Midgley <bmidgley@xmission.com> + * + * + *  This library is free software; you can redistribute it and/or + *  modify it under the terms of the GNU Lesser General Public + *  License as published by the Free Software Foundation; either + *  version 2.1 of the License, or (at your option) any later version. + * + *  This library is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + *  Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this library; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#define fabs(x) ((x) < 0 ? -(x) : (x)) +/* C does not provide an explicit arithmetic shift right but this will +   always be correct and every compiler *should* generate optimal code */ +#define ASR(val, bits) ((-2 >> 1 == -1) ? \ +		 ((int32_t)(val)) >> (bits) : ((int32_t) (val)) / (1 << (bits))) + +#define SCALE_PROTO4_TBL	15 +#define SCALE_ANA4_TBL		17 +#define SCALE_PROTO8_TBL	16 +#define SCALE_ANA8_TBL		17 +#define SCALE_SPROTO4_TBL	12 +#define SCALE_SPROTO8_TBL	14 +#define SCALE_NPROTO4_TBL	11 +#define SCALE_NPROTO8_TBL	11 +#define SCALE4_STAGE1_BITS	15 +#define SCALE4_STAGE2_BITS	16 +#define SCALE4_STAGED1_BITS	15 +#define SCALE4_STAGED2_BITS	16 +#define SCALE8_STAGE1_BITS	15 +#define SCALE8_STAGE2_BITS	15 +#define SCALE8_STAGED1_BITS	15 +#define SCALE8_STAGED2_BITS	16 + +typedef int32_t sbc_fixed_t; + +#define SCALE4_STAGE1(src)  ASR(src, SCALE4_STAGE1_BITS) +#define SCALE4_STAGE2(src)  ASR(src, SCALE4_STAGE2_BITS) +#define SCALE4_STAGED1(src) ASR(src, SCALE4_STAGED1_BITS) +#define SCALE4_STAGED2(src) ASR(src, SCALE4_STAGED2_BITS) +#define SCALE8_STAGE1(src)  ASR(src, SCALE8_STAGE1_BITS) +#define SCALE8_STAGE2(src)  ASR(src, SCALE8_STAGE2_BITS) +#define SCALE8_STAGED1(src) ASR(src, SCALE8_STAGED1_BITS) +#define SCALE8_STAGED2(src) ASR(src, SCALE8_STAGED2_BITS) + +#define SBC_FIXED_0(val) { val = 0; } +#define MUL(a, b)        ((a) * (b)) +#ifdef __arm__ +#define MULA(a, b, res) ({				\ +		int tmp = res;			\ +		__asm__(				\ +			"mla %0, %2, %3, %0"		\ +			: "=&r" (tmp)			\ +			: "0" (tmp), "r" (a), "r" (b));	\ +		tmp; }) +#else +#define MULA(a, b, res)  ((a) * (b) + (res)) +#endif diff --git a/src/modules/bluetooth/sbc_tables.h b/src/modules/bluetooth/sbc_tables.h new file mode 100644 index 00000000..7ac4e68b --- /dev/null +++ b/src/modules/bluetooth/sbc_tables.h @@ -0,0 +1,167 @@ +/* + * + *  Bluetooth low-complexity, subband codec (SBC) library + * + *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org> + *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch> + *  Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com> + * + * + *  This library is free software; you can redistribute it and/or + *  modify it under the terms of the GNU Lesser General Public + *  License as published by the Free Software Foundation; either + *  version 2.1 of the License, or (at your option) any later version. + * + *  This library is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + *  Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this library; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +/* A2DP specification: Appendix B, page 69 */ +static const int sbc_offset4[4][4] = { +	{ -1, 0, 0, 0 }, +	{ -2, 0, 0, 1 }, +	{ -2, 0, 0, 1 }, +	{ -2, 0, 0, 1 } +}; + +/* A2DP specification: Appendix B, page 69 */ +static const int sbc_offset8[4][8] = { +	{ -2, 0, 0, 0, 0, 0, 0, 1 }, +	{ -3, 0, 0, 0, 0, 0, 1, 2 }, +	{ -4, 0, 0, 0, 0, 0, 1, 2 }, +	{ -4, 0, 0, 0, 0, 0, 1, 2 } +}; + +#define SP4(val) ASR(val, SCALE_PROTO4_TBL) +#define SA4(val) ASR(val, SCALE_ANA4_TBL) +#define SP8(val) ASR(val, SCALE_PROTO8_TBL) +#define SA8(val) ASR(val, SCALE_ANA8_TBL) +#define SS4(val) ASR(val, SCALE_SPROTO4_TBL) +#define SS8(val) ASR(val, SCALE_SPROTO8_TBL) +#define SN4(val) ASR(val, SCALE_NPROTO4_TBL) +#define SN8(val) ASR(val, SCALE_NPROTO8_TBL) + +static const int32_t _sbc_proto_4[20] = { +	SP4(0x02cb3e8c), SP4(0x22b63dc0), SP4(0x002329cc), SP4(0x053b7548), +	SP4(0x31eab940), SP4(0xec1f5e60), SP4(0xff3773a8), SP4(0x0061c5a7), +	SP4(0x07646680), SP4(0x3f239480), SP4(0xf89f23a8), SP4(0x007a4737), +	SP4(0x00b32807), SP4(0x083ddc80), SP4(0x4825e480), SP4(0x0191e578), +	SP4(0x00ff11ca), SP4(0x00fb7991), SP4(0x069fdc58), SP4(0x4b584000) +}; + +static const int32_t _anamatrix4[4] = { +	SA4(0x2d413cc0), SA4(0x3b20d780), SA4(0x40000000), SA4(0x187de2a0) +}; + +static const int32_t _sbc_proto_8[40] = { +	SP8(0x02e5cd20), SP8(0x22d0c200), SP8(0x006bfe27), SP8(0x07808930), +	SP8(0x3f1c8800), SP8(0xf8810d70), SP8(0x002cfdc6), SP8(0x055acf28), +	SP8(0x31f566c0), SP8(0xebfe57e0), SP8(0xff27c437), SP8(0x001485cc), +	SP8(0x041c6e58), SP8(0x2a7cfa80), SP8(0xe4c4a240), SP8(0xfe359e4c), +	SP8(0x0048b1f8), SP8(0x0686ce30), SP8(0x38eec5c0), SP8(0xf2a1b9f0), +	SP8(0xffe8904a), SP8(0x0095698a), SP8(0x0824a480), SP8(0x443b3c00), +	SP8(0xfd7badc8), SP8(0x00d3e2d9), SP8(0x00c183d2), SP8(0x084e1950), +	SP8(0x4810d800), SP8(0x017f43fe), SP8(0x01056dd8), SP8(0x00e9cb9f), +	SP8(0x07d7d090), SP8(0x4a708980), SP8(0x0488fae8), SP8(0x0113bd20), +	SP8(0x0107b1a8), SP8(0x069fb3c0), SP8(0x4b3db200), SP8(0x00763f48) +}; + +static const int32_t sbc_proto_4_40m0[] = { +	SS4(0x00000000), SS4(0xffa6982f), SS4(0xfba93848), SS4(0x0456c7b8), +	SS4(0x005967d1), SS4(0xfffb9ac7), SS4(0xff589157), SS4(0xf9c2a8d8), +	SS4(0x027c1434), SS4(0x0019118b), SS4(0xfff3c74c), SS4(0xff137330), +	SS4(0xf81b8d70), SS4(0x00ec1b8b), SS4(0xfff0b71a), SS4(0xffe99b00), +	SS4(0xfef84470), SS4(0xf6fb4370), SS4(0xffcdc351), SS4(0xffe01dc7) +}; + +static const int32_t sbc_proto_4_40m1[] = { +	SS4(0xffe090ce), SS4(0xff2c0475), SS4(0xf694f800), SS4(0xff2c0475), +	SS4(0xffe090ce), SS4(0xffe01dc7), SS4(0xffcdc351), SS4(0xf6fb4370), +	SS4(0xfef84470), SS4(0xffe99b00), SS4(0xfff0b71a), SS4(0x00ec1b8b), +	SS4(0xf81b8d70), SS4(0xff137330), SS4(0xfff3c74c), SS4(0x0019118b), +	SS4(0x027c1434), SS4(0xf9c2a8d8), SS4(0xff589157), SS4(0xfffb9ac7) +}; + +static const int32_t sbc_proto_8_80m0[] = { +	SS8(0x00000000), SS8(0xfe8d1970), SS8(0xee979f00), SS8(0x11686100), +	SS8(0x0172e690), SS8(0xfff5bd1a), SS8(0xfdf1c8d4), SS8(0xeac182c0), +	SS8(0x0d9daee0), SS8(0x00e530da), SS8(0xffe9811d), SS8(0xfd52986c), +	SS8(0xe7054ca0), SS8(0x0a00d410), SS8(0x006c1de4), SS8(0xffdba705), +	SS8(0xfcbc98e8), SS8(0xe3889d20), SS8(0x06af2308), SS8(0x000bb7db), +	SS8(0xffca00ed), SS8(0xfc3fbb68), SS8(0xe071bc00), SS8(0x03bf7948), +	SS8(0xffc4e05c), SS8(0xffb54b3b), SS8(0xfbedadc0), SS8(0xdde26200), +	SS8(0x0142291c), SS8(0xff960e94), SS8(0xff9f3e17), SS8(0xfbd8f358), +	SS8(0xdbf79400), SS8(0xff405e01), SS8(0xff7d4914), SS8(0xff8b1a31), +	SS8(0xfc1417b8), SS8(0xdac7bb40), SS8(0xfdbb828c), SS8(0xff762170) +}; + +static const int32_t sbc_proto_8_80m1[] = { +	SS8(0xff7c272c), SS8(0xfcb02620), SS8(0xda612700), SS8(0xfcb02620), +	SS8(0xff7c272c), SS8(0xff762170), SS8(0xfdbb828c), SS8(0xdac7bb40), +	SS8(0xfc1417b8), SS8(0xff8b1a31), SS8(0xff7d4914), SS8(0xff405e01), +	SS8(0xdbf79400), SS8(0xfbd8f358), SS8(0xff9f3e17), SS8(0xff960e94), +	SS8(0x0142291c), SS8(0xdde26200), SS8(0xfbedadc0), SS8(0xffb54b3b), +	SS8(0xffc4e05c), SS8(0x03bf7948), SS8(0xe071bc00), SS8(0xfc3fbb68), +	SS8(0xffca00ed), SS8(0x000bb7db), SS8(0x06af2308), SS8(0xe3889d20), +	SS8(0xfcbc98e8), SS8(0xffdba705), SS8(0x006c1de4), SS8(0x0a00d410), +	SS8(0xe7054ca0), SS8(0xfd52986c), SS8(0xffe9811d), SS8(0x00e530da), +	SS8(0x0d9daee0), SS8(0xeac182c0), SS8(0xfdf1c8d4), SS8(0xfff5bd1a) +}; + +static const int32_t _anamatrix8[8] = { +	SA8(0x3b20d780), SA8(0x187de2a0), SA8(0x3ec52f80), SA8(0x3536cc40), +	SA8(0x238e7680), SA8(0x0c7c5c20), SA8(0x2d413cc0), SA8(0x40000000) +}; + +static const int32_t synmatrix4[8][4] = { +	{ SN4(0x05a82798), SN4(0xfa57d868), SN4(0xfa57d868), SN4(0x05a82798) }, +	{ SN4(0x030fbc54), SN4(0xf89be510), SN4(0x07641af0), SN4(0xfcf043ac) }, +	{ SN4(0x00000000), SN4(0x00000000), SN4(0x00000000), SN4(0x00000000) }, +	{ SN4(0xfcf043ac), SN4(0x07641af0), SN4(0xf89be510), SN4(0x030fbc54) }, +	{ SN4(0xfa57d868), SN4(0x05a82798), SN4(0x05a82798), SN4(0xfa57d868) }, +	{ SN4(0xf89be510), SN4(0xfcf043ac), SN4(0x030fbc54), SN4(0x07641af0) }, +	{ SN4(0xf8000000), SN4(0xf8000000), SN4(0xf8000000), SN4(0xf8000000) }, +	{ SN4(0xf89be510), SN4(0xfcf043ac), SN4(0x030fbc54), SN4(0x07641af0) } +}; + +static const int32_t synmatrix8[16][8] = { +	{ SN8(0x05a82798), SN8(0xfa57d868), SN8(0xfa57d868), SN8(0x05a82798), +	  SN8(0x05a82798), SN8(0xfa57d868), SN8(0xfa57d868), SN8(0x05a82798) }, +	{ SN8(0x0471ced0), SN8(0xf8275a10), SN8(0x018f8b84), SN8(0x06a6d988), +	  SN8(0xf9592678), SN8(0xfe70747c), SN8(0x07d8a5f0), SN8(0xfb8e3130) }, +	{ SN8(0x030fbc54), SN8(0xf89be510), SN8(0x07641af0), SN8(0xfcf043ac), +	  SN8(0xfcf043ac), SN8(0x07641af0), SN8(0xf89be510), SN8(0x030fbc54) }, +	{ SN8(0x018f8b84), SN8(0xfb8e3130), SN8(0x06a6d988), SN8(0xf8275a10), +	  SN8(0x07d8a5f0), SN8(0xf9592678), SN8(0x0471ced0), SN8(0xfe70747c) }, +	{ SN8(0x00000000), SN8(0x00000000), SN8(0x00000000), SN8(0x00000000), +	  SN8(0x00000000), SN8(0x00000000), SN8(0x00000000), SN8(0x00000000) }, +	{ SN8(0xfe70747c), SN8(0x0471ced0), SN8(0xf9592678), SN8(0x07d8a5f0), +	  SN8(0xf8275a10), SN8(0x06a6d988), SN8(0xfb8e3130), SN8(0x018f8b84) }, +	{ SN8(0xfcf043ac), SN8(0x07641af0), SN8(0xf89be510), SN8(0x030fbc54), +	  SN8(0x030fbc54), SN8(0xf89be510), SN8(0x07641af0), SN8(0xfcf043ac) }, +	{ SN8(0xfb8e3130), SN8(0x07d8a5f0), SN8(0xfe70747c), SN8(0xf9592678), +	  SN8(0x06a6d988), SN8(0x018f8b84), SN8(0xf8275a10), SN8(0x0471ced0) }, +	{ SN8(0xfa57d868), SN8(0x05a82798), SN8(0x05a82798), SN8(0xfa57d868), +	  SN8(0xfa57d868), SN8(0x05a82798), SN8(0x05a82798), SN8(0xfa57d868) }, +	{ SN8(0xf9592678), SN8(0x018f8b84), SN8(0x07d8a5f0), SN8(0x0471ced0), +	  SN8(0xfb8e3130), SN8(0xf8275a10), SN8(0xfe70747c), SN8(0x06a6d988) }, +	{ SN8(0xf89be510), SN8(0xfcf043ac), SN8(0x030fbc54), SN8(0x07641af0), +	  SN8(0x07641af0), SN8(0x030fbc54), SN8(0xfcf043ac), SN8(0xf89be510) }, +	{ SN8(0xf8275a10), SN8(0xf9592678), SN8(0xfb8e3130), SN8(0xfe70747c), +	  SN8(0x018f8b84), SN8(0x0471ced0), SN8(0x06a6d988), SN8(0x07d8a5f0) }, +	{ SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000), +	  SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000) }, +	{ SN8(0xf8275a10), SN8(0xf9592678), SN8(0xfb8e3130), SN8(0xfe70747c), +	  SN8(0x018f8b84), SN8(0x0471ced0), SN8(0x06a6d988), SN8(0x07d8a5f0) }, +	{ SN8(0xf89be510), SN8(0xfcf043ac), SN8(0x030fbc54), SN8(0x07641af0), +	  SN8(0x07641af0), SN8(0x030fbc54), SN8(0xfcf043ac), SN8(0xf89be510) }, +	{ SN8(0xf9592678), SN8(0x018f8b84), SN8(0x07d8a5f0), SN8(0x0471ced0), +	  SN8(0xfb8e3130), SN8(0xf8275a10), SN8(0xfe70747c), SN8(0x06a6d988) } +}; diff --git a/src/modules/dbus-util.c b/src/modules/dbus-util.c index 8e0066bc..d2abf087 100644 --- a/src/modules/dbus-util.c +++ b/src/modules/dbus-util.c @@ -90,7 +90,7 @@ static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {  }  /* pa_io_event_cb_t IO event handler */ -static void handle_io_event(PA_GCC_UNUSED pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { +static void handle_io_event(pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {      unsigned int flags = 0;      DBusWatch *watch = userdata; @@ -126,7 +126,7 @@ static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struc          dbus_timeout_handle(timeout);          /* restart it for the next scheduled time */ -        pa_timeval_add(&next, dbus_timeout_get_interval(timeout) * 1000); +        pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);          ea->time_restart(e, &next);      }  } @@ -192,7 +192,7 @@ static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {          return FALSE;      pa_gettimeofday(&tv); -    pa_timeval_add(&tv, dbus_timeout_get_interval(timeout) * 1000); +    pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);      ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event, timeout); @@ -227,7 +227,7 @@ static void toggle_timeout(DBusTimeout *timeout, void *data) {          struct timeval tv;          pa_gettimeofday(&tv); -        pa_timeval_add(&tv, dbus_timeout_get_interval(timeout) * 1000); +        pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);          c->mainloop->time_restart(ev, &tv);      } else diff --git a/src/modules/gconf/Makefile b/src/modules/gconf/Makefile index 316beb72..efe5a336 100644..120000 --- a/src/modules/gconf/Makefile +++ b/src/modules/gconf/Makefile @@ -1,13 +1 @@ -# This is a dirty trick just to ease compilation with emacs -# -# This file is not intended to be distributed or anything -# -# So: don't touch it, even better ignore it! - -all: -	$(MAKE) -C ../.. - -clean: -	$(MAKE) -C ../.. clean - -.PHONY: all clean +../../pulse/Makefile
\ No newline at end of file diff --git a/src/modules/gconf/module-gconf.c b/src/modules/gconf/module-gconf.c index e2b0f7c0..845ede50 100644 --- a/src/modules/gconf/module-gconf.c +++ b/src/modules/gconf/module-gconf.c @@ -96,7 +96,7 @@ static int fill_buf(struct userdata *u) {      if ((r = pa_read(u->fd, u->buf + u->buf_fill, BUF_MAX - u->buf_fill, &u->fd_type)) <= 0)          return -1; -    u->buf_fill += r; +    u->buf_fill += (size_t) r;      return 0;  } @@ -123,7 +123,7 @@ static char *read_string(struct userdata *u) {          if ((e = memchr(u->buf, 0, u->buf_fill))) {              char *ret = pa_xstrdup(u->buf); -            u->buf_fill -= e - u->buf +1; +            u->buf_fill -= (size_t) (e - u->buf +1);              memmove(u->buf, e+1, u->buf_fill);              return ret;          } @@ -164,10 +164,10 @@ static void unload_all_modules(struct userdata *u, struct module_info*m) {  static void load_module(          struct userdata *u,          struct module_info *m, -        int i, +        unsigned i,          const char *name,          const char *args, -        int is_new) { +        pa_bool_t is_new) {      pa_module *mod; @@ -378,7 +378,16 @@ void pa__done(pa_module*m) {      if (u->pid != (pid_t) -1) {          kill(u->pid, SIGTERM); -        waitpid(u->pid, NULL, 0); + +        for (;;) { +            if (waitpid(u->pid, NULL, 0) >= 0) +                break; + +            if (errno != EINTR) { +                pa_log("waitpid() failed: %s", pa_cstrerror(errno)); +                break; +            } +        }      }      if (u->io_event) @@ -387,7 +396,6 @@ void pa__done(pa_module*m) {      if (u->fd >= 0)          pa_close(u->fd); -      if (u->module_infos)          pa_hashmap_free(u->module_infos, module_info_free, u); diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index aad6801e..6dea172f 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -28,6 +28,10 @@  #include <asoundlib.h> +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include <valgrind/memcheck.h> +#endif +  #include <pulse/xmalloc.h>  #include <pulse/util.h>  #include <pulse/timeval.h> @@ -68,8 +72,7 @@ PA_MODULE_USAGE(          "mmap=<enable memory mapping?> "          "tsched=<enable system timer based scheduling mode?> "          "tsched_buffer_size=<buffer size when using timer based scheduling> " -        "tsched_buffer_watermark=<lower fill watermark> " -        "mixer_reset=<reset hw volume and mute settings to sane defaults when falling back to software?>"); +        "tsched_buffer_watermark=<lower fill watermark>");  static const char* const valid_modargs[] = {      "sink_name", @@ -85,7 +88,6 @@ static const char* const valid_modargs[] = {      "tsched",      "tsched_buffer_size",      "tsched_buffer_watermark", -    "mixer_reset",      NULL  }; @@ -112,6 +114,8 @@ struct userdata {      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_cvolume hardware_volume;      size_t frame_size, fragment_size, hwbuf_size, tsched_watermark;      unsigned nfragments; @@ -139,7 +143,7 @@ static void fix_tsched_watermark(struct userdata *u) {      size_t min_sleep, min_wakeup;      pa_assert(u); -    max_use = u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size; +    max_use = u->hwbuf_size - (size_t) u->hwbuf_unused_frames * u->frame_size;      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); @@ -212,8 +216,8 @@ static int try_recover(struct userdata *u, const char *call, int err) {  static size_t check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) {      size_t left_to_play; -    if (n*u->frame_size < u->hwbuf_size) -        left_to_play = u->hwbuf_size - (n*u->frame_size); +    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; @@ -237,9 +241,9 @@ static size_t check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) {      return left_to_play;  } -static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) { +static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {      int work_done = 0; -    pa_usec_t max_sleep_usec, process_usec; +    pa_usec_t max_sleep_usec = 0, process_usec = 0;      size_t left_to_play;      pa_assert(u); @@ -257,9 +261,9 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {          /* First we determine how many samples are missing to fill the           * buffer up to 100% */ -        if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { +        if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) { -            if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0) +            if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)                  continue;              return r; @@ -275,14 +279,23 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {              * need to guarantee that clients only have to keep around              * a single hw buffer length. */ -            if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2) +            if (!polled && +                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 <= u->hwbuf_unused_frames)) { + +            if (polled) +                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 PulseAudio developers."); +              break; +        }          n -= u->hwbuf_unused_frames; +        polled = FALSE; +  /*         pa_log_debug("Filling up"); */          for (;;) { @@ -291,10 +304,11 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {              int err;              const snd_pcm_channel_area_t *areas;              snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n; +            snd_pcm_sframes_t sframes;  /*             pa_log_debug("%lu frames to write", (unsigned long) frames); */ -            if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { +            if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {                  if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)                      continue; @@ -326,9 +340,9 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {               * a little bit longer around? */              pa_memblock_unref_fixed(chunk.memblock); -            if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { +            if (PA_UNLIKELY((sframes = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { -                if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0) +                if ((r = try_recover(u, "snd_pcm_mmap_commit", (int) sframes)) == 0)                      continue;                  return r; @@ -336,7 +350,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {              work_done = 1; -            u->frame_index += frames; +            u->frame_index += (int64_t) frames;              u->since_start += frames * u->frame_size;  /*             pa_log_debug("wrote %lu frames", (unsigned long) frames); */ @@ -344,7 +358,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {              if (frames >= (snd_pcm_uframes_t) n)                  break; -            n -= frames; +            n -= (snd_pcm_sframes_t) frames;          }      } @@ -352,9 +366,9 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {      return work_done;  } -static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) { +static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {      int work_done = 0; -    pa_usec_t max_sleep_usec, process_usec; +    pa_usec_t max_sleep_usec = 0, process_usec = 0;      size_t left_to_play;      pa_assert(u); @@ -369,9 +383,9 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) {          snd_pcm_hwsync(u->pcm_handle); -        if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { +        if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) { -            if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0) +            if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)                  continue;              return r; @@ -387,14 +401,23 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) {              * need to guarantee that clients only have to keep around              * a single hw buffer length. */ -            if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2) +            if (!polled && +                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 <= u->hwbuf_unused_frames)) { + +            if (polled) +                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 PulseAudio developers."); +              break; +        }          n -= u->hwbuf_unused_frames; +        polled = FALSE; +          for (;;) {              snd_pcm_sframes_t frames;              void *p; @@ -402,31 +425,31 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) {  /*         pa_log_debug("%lu frames to write", (unsigned long) frames); */              if (u->memchunk.length <= 0) -                pa_sink_render(u->sink, n * u->frame_size, &u->memchunk); +                pa_sink_render(u->sink, (size_t) n * u->frame_size, &u->memchunk);              pa_assert(u->memchunk.length > 0); -            frames = u->memchunk.length / u->frame_size; +            frames = (snd_pcm_sframes_t) (u->memchunk.length / u->frame_size);              if (frames > n)                  frames = n;              p = pa_memblock_acquire(u->memchunk.memblock); -            frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, frames); +            frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, (snd_pcm_uframes_t) frames);              pa_memblock_release(u->memchunk.memblock);              pa_assert(frames != 0);              if (PA_UNLIKELY(frames < 0)) { -                if ((r = try_recover(u, "snd_pcm_writei", n)) == 0) +                if ((r = try_recover(u, "snd_pcm_writei", (int) frames)) == 0)                      continue;                  return r;              } -            u->memchunk.index += frames * u->frame_size; -            u->memchunk.length -= frames * u->frame_size; +            u->memchunk.index += (size_t) frames * u->frame_size; +            u->memchunk.length -= (size_t) frames * u->frame_size;              if (u->memchunk.length <= 0) {                  pa_memblock_unref(u->memchunk.memblock); @@ -436,7 +459,7 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) {              work_done = 1;              u->frame_index += frames; -            u->since_start += frames * u->frame_size; +            u->since_start += (size_t) frames * u->frame_size;  /*         pa_log_debug("wrote %lu frames", (unsigned long) frames); */ @@ -490,7 +513,7 @@ static void update_smoother(struct userdata *u) {  /*     now1 = pa_timeval_load(×tamp); */      now1 = pa_rtclock_usec(); -    now2 = pa_bytes_to_usec(frames * u->frame_size, &u->sink->sample_spec); +    now2 = pa_bytes_to_usec((uint64_t) frames * u->frame_size, &u->sink->sample_spec);      pa_smoother_put(u->smoother, now1, now2);  } @@ -504,7 +527,7 @@ 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(u->frame_index * u->frame_size, &u->sink->sample_spec) - (int64_t) now2; +    delay = (int64_t) pa_bytes_to_usec((uint64_t) u->frame_index * u->frame_size, &u->sink->sample_spec) - (int64_t) now2;      if (delay > 0)          r = (pa_usec_t) delay; @@ -534,8 +557,8 @@ static int suspend(struct userdata *u) {      pa_smoother_pause(u->smoother, pa_rtclock_usec()); -    /* Let's suspend */ -    snd_pcm_drain(u->pcm_handle); +    /* Let's suspend -- we don't call snd_pcm_drain() here since that might +     * take awfully long with our long buffer sizes today. */      snd_pcm_close(u->pcm_handle);      u->pcm_handle = NULL; @@ -564,7 +587,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.2f", (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); @@ -573,9 +596,9 @@ static int update_sw_params(struct userdata *u) {              if (PA_UNLIKELY(b < u->frame_size))                  b = u->frame_size; -            u->hwbuf_unused_frames = -                PA_LIKELY(b < u->hwbuf_size) ? -                ((u->hwbuf_size - b) / u->frame_size) : 0; +            u->hwbuf_unused_frames = (snd_pcm_sframes_t) +                (PA_LIKELY(b < u->hwbuf_size) ? +                 ((u->hwbuf_size - b) / u->frame_size) : 0);              fix_tsched_watermark(u);          } @@ -584,7 +607,7 @@ static int update_sw_params(struct userdata *u) {      pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames);      /* We need at last one frame in the used part of the buffer */ -    avail_min = u->hwbuf_unused_frames + 1; +    avail_min = (snd_pcm_uframes_t) u->hwbuf_unused_frames + 1;      if (u->use_tsched) {          pa_usec_t sleep_usec, process_usec; @@ -600,7 +623,7 @@ static int update_sw_params(struct userdata *u) {          return err;      } -    pa_sink_set_max_request(u->sink, u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size); +    pa_sink_set_max_request(u->sink, u->hwbuf_size - (size_t) u->hwbuf_unused_frames * u->frame_size);      return 0;  } @@ -618,7 +641,11 @@ static int unsuspend(struct userdata *u) {      pa_log_info("Trying resume...");      snd_config_update_free_global(); -    if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { +    if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_PLAYBACK, +                            /*SND_PCM_NONBLOCK|*/ +                            SND_PCM_NO_AUTO_RESAMPLE| +                            SND_PCM_NO_AUTO_CHANNELS| +                            SND_PCM_NO_AUTO_FORMAT)) < 0) {          pa_log("Error opening PCM device %s: %s", u->device_name, snd_strerror(err));          goto fail;      } @@ -645,7 +672,9 @@ static int unsuspend(struct userdata *u) {      }      if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) { -        pa_log_warn("Resume failed, couldn't restore original fragment settings."); +        pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu*%lu, New %lu*%lu)", +                    (unsigned long) u->nfragments, (unsigned long) u->fragment_size, +                    (unsigned long) nfrags, period_size * u->frame_size);          goto fail;      } @@ -737,40 +766,101 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {          return 0;      if (mask & SND_CTL_EVENT_MASK_VALUE) { -        pa_sink_get_volume(u->sink); -        pa_sink_get_mute(u->sink); +        pa_sink_get_volume(u->sink, TRUE); +        pa_sink_get_mute(u->sink, TRUE);      }      return 0;  } +static pa_volume_t from_alsa_volume(struct userdata *u, long alsa_vol) { + +    return (pa_volume_t) round(((double) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / +                               (double) (u->hw_volume_max - u->hw_volume_min)); +} + +static long to_alsa_volume(struct userdata *u, pa_volume_t vol) { +    long alsa_vol; + +    alsa_vol = (long) round(((double) vol * (double) (u->hw_volume_max - u->hw_volume_min)) +                            / PA_VOLUME_NORM) + u->hw_volume_min; + +    return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); +} +  static int sink_get_volume_cb(pa_sink *s) {      struct userdata *u = s->userdata;      int err; -    int i; +    unsigned i; +    pa_cvolume r; +    char t[PA_CVOLUME_SNPRINT_MAX];      pa_assert(u);      pa_assert(u->mixer_elem); -    for (i = 0; i < s->sample_spec.channels; i++) { -        long alsa_vol; +    if (u->mixer_seperate_channels) { -        pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, u->mixer_map[i])); +        r.channels = s->sample_spec.channels; -        if (u->hw_dB_supported) { +        for (i = 0; i < s->sample_spec.channels; i++) { +            long alsa_vol; -            if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) >= 0) { -                s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0); -                continue; +            if (u->hw_dB_supported) { + +                if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) +                    goto fail; + +#ifdef HAVE_VALGRIND_MEMCHECK_H +                VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); +#endif + +                r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0); +            } else { + +                if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) +                    goto fail; + +                r.values[i] = from_alsa_volume(u, alsa_vol);              } +        } + +    } else { +        long alsa_vol; + +        if (u->hw_dB_supported) { + +            if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) +                goto fail; + +#ifdef HAVE_VALGRIND_MEMCHECK_H +            VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); +#endif + +            pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0)); + +        } else { + +            if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) +                goto fail; -            u->hw_dB_supported = FALSE; +            pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol));          } +    } -        if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) -            goto fail; +    pa_log_debug("Read hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); + +    if (!pa_cvolume_equal(&u->hardware_volume, &r)) { + +        u->hardware_volume = s->volume = r; + +        if (u->hw_dB_supported) { +            pa_cvolume reset; + +            /* Hmm, so the hardware volume changed, let's reset our software volume */ -        s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); +            pa_cvolume_reset(&reset, s->sample_spec.channels); +            pa_sink_set_soft_volume(s, &reset); +        }      }      return 0; @@ -784,44 +874,101 @@ fail:  static int sink_set_volume_cb(pa_sink *s) {      struct userdata *u = s->userdata;      int err; -    int i; +    unsigned i; +    pa_cvolume r;      pa_assert(u);      pa_assert(u->mixer_elem); -    for (i = 0; i < s->sample_spec.channels; i++) { -        long alsa_vol; -        pa_volume_t vol; +    if (u->mixer_seperate_channels) { + +        r.channels = s->sample_spec.channels; + +        for (i = 0; i < s->sample_spec.channels; i++) { +            long alsa_vol; +            pa_volume_t vol; + +            vol = s->volume.values[i]; + +            if (u->hw_dB_supported) { -        pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, u->mixer_map[i])); +                alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); +                alsa_vol += u->hw_dB_max; +                alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); -        vol = PA_MIN(s->volume.values[i], PA_VOLUME_NORM); +                if ((err = snd_mixer_selem_set_playback_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, 1)) < 0) +                    goto fail; + +                if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) +                    goto fail; + +                r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0); + +            } else { +                alsa_vol = to_alsa_volume(u, vol); + +                if ((err = snd_mixer_selem_set_playback_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0) +                    goto fail; + +                if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) +                    goto fail; + +                r.values[i] = from_alsa_volume(u, alsa_vol); +            } +        } + +    } else { +        pa_volume_t vol; +        long alsa_vol; + +        vol = pa_cvolume_max(&s->volume);          if (u->hw_dB_supported) {              alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); +            alsa_vol += u->hw_dB_max;              alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); -            if ((err = snd_mixer_selem_set_playback_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, -1)) >= 0) { +            if ((err = snd_mixer_selem_set_playback_dB_all(u->mixer_elem, alsa_vol, 1)) < 0) +                goto fail; + +            if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) +                goto fail; -                if (snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0) -                    s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0); +            pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0)); -                continue; -            } +        } else { +            alsa_vol = to_alsa_volume(u, vol); -            u->hw_dB_supported = FALSE; +            if ((err = snd_mixer_selem_set_playback_volume_all(u->mixer_elem, alsa_vol)) < 0) +                goto fail; +            if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) +                goto fail; + +            pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol));          } +    } -        alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; -        alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); +    u->hardware_volume = r; -        if ((err = snd_mixer_selem_set_playback_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0) -            goto fail; +    if (u->hw_dB_supported) { +        char t[PA_CVOLUME_SNPRINT_MAX]; -        if (snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0) -            s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); -    } +        /* Match exactly what the user requested by software */ + +        pa_sw_cvolume_divide(&r, &s->volume, &r); +        pa_sink_set_soft_volume(s, &r); + +        pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume)); +        pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); +        pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); + +    } else + +        /* We can't match exactly what the user requested, hence let's +         * at least tell the user about it */ + +        s->volume = r;      return 0; @@ -903,7 +1050,7 @@ static int process_rewind(struct userdata *u) {      snd_pcm_hwsync(u->pcm_handle);      if ((unused = snd_pcm_avail_update(u->pcm_handle)) < 0) { -        pa_log("snd_pcm_avail_update() failed: %s", snd_strerror(unused)); +        pa_log("snd_pcm_avail_update() failed: %s", snd_strerror((int) unused));          return -1;      } @@ -922,15 +1069,15 @@ static int process_rewind(struct userdata *u) {          pa_log_debug("Limited to %lu bytes.", (unsigned long) rewind_nbytes); -        in_frames = (snd_pcm_sframes_t) rewind_nbytes / u->frame_size; +        in_frames = (snd_pcm_sframes_t) (rewind_nbytes / u->frame_size);          pa_log_debug("before: %lu", (unsigned long) in_frames); -        if ((out_frames = snd_pcm_rewind(u->pcm_handle, in_frames)) < 0) { -            pa_log("snd_pcm_rewind() failed: %s", snd_strerror(out_frames)); +        if ((out_frames = snd_pcm_rewind(u->pcm_handle, (snd_pcm_uframes_t) in_frames)) < 0) { +            pa_log("snd_pcm_rewind() failed: %s", snd_strerror((int) out_frames));              return -1;          }          pa_log_debug("after: %lu", (unsigned long) out_frames); -        rewind_nbytes = out_frames * u->frame_size; +        rewind_nbytes = (size_t) out_frames * u->frame_size;          if (rewind_nbytes <= 0)              pa_log_info("Tried rewind, but was apparently not possible."); @@ -955,6 +1102,7 @@ finish:  static void thread_func(void *userdata) {      struct userdata *u = userdata; +    unsigned short revents = 0;      pa_assert(u); @@ -974,16 +1122,16 @@ static void thread_func(void *userdata) {          /* Render some data and write it to the dsp */          if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {              int work_done; -            pa_usec_t sleep_usec; +            pa_usec_t sleep_usec = 0;              if (u->sink->thread_info.rewind_requested)                  if (process_rewind(u) < 0)                          goto fail;              if (u->use_mmap) -                work_done = mmap_write(u, &sleep_usec); +                work_done = mmap_write(u, &sleep_usec, revents & POLLOUT);              else -                work_done = unix_write(u, &sleep_usec); +                work_done = unix_write(u, &sleep_usec, revents & POLLOUT);              if (work_done < 0)                  goto fail; @@ -1042,7 +1190,7 @@ static void thread_func(void *userdata) {              pa_rtpoll_set_timer_disabled(u->rtpoll);          /* Hmm, nothing to do. Let's sleep */ -        if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0) +        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)              goto fail;          if (ret == 0) @@ -1051,7 +1199,6 @@ static void thread_func(void *userdata) {          /* Tell ALSA about this and process its response */          if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {              struct pollfd *pollfd; -            unsigned short revents = 0;              int err;              unsigned n; @@ -1062,7 +1209,7 @@ static void thread_func(void *userdata) {                  goto fail;              } -            if (revents & (POLLERR|POLLNVAL|POLLHUP)) { +            if (revents & (POLLIN|POLLERR|POLLNVAL|POLLHUP|POLLPRI)) {                  if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)                      goto fail; @@ -1071,8 +1218,9 @@ static void thread_func(void *userdata) {              }              if (revents && u->use_tsched) -                pa_log_debug("Wakeup from ALSA! (%i)", revents); -        } +                pa_log_debug("Wakeup from ALSA!%s%s", (revents & POLLIN) ? " INPUT" : "", (revents & POLLOUT) ? " OUTPUT" : ""); +        } else +            revents = 0;      }  fail: @@ -1100,7 +1248,7 @@ int pa__init(pa_module*m) {      const char *name;      char *name_buf = NULL;      pa_bool_t namereg_fail; -    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, mixer_reset = TRUE; +    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;      pa_usec_t usec;      pa_sink_new_data data; @@ -1124,11 +1272,11 @@ int pa__init(pa_module*m) {      frame_size = pa_frame_size(&ss);      nfrags = m->core->default_n_fragments; -    frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss); +    frag_size = (uint32_t) pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss);      if (frag_size <= 0) -        frag_size = frame_size; -    tsched_size = pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss); -    tsched_watermark = pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss); +        frag_size = (uint32_t) frame_size; +    tsched_size = (uint32_t) pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss); +    tsched_watermark = (uint32_t) pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss);      if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 ||          pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 || @@ -1153,15 +1301,10 @@ int pa__init(pa_module*m) {      }      if (use_tsched && !pa_rtclock_hrtimer()) { -        pa_log("Disabling timer-based scheduling because high-resolution timers are not available from the kernel."); +        pa_log_notice("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");          use_tsched = FALSE;      } -    if (pa_modargs_get_value_boolean(ma, "mixer_reset", &mixer_reset) < 0) { -        pa_log("Failed to parse mixer_reset argument."); -        goto fail; -    } -      u = pa_xnew0(struct userdata, 1);      u->core = m->core;      u->module = m; @@ -1313,7 +1456,7 @@ int pa__init(pa_module*m) {      pa_sink_set_rtpoll(u->sink, u->rtpoll);      u->frame_size = frame_size; -    u->fragment_size = frag_size = period_frames * frame_size; +    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; @@ -1322,6 +1465,8 @@ int pa__init(pa_module*m) {      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)          fix_tsched_watermark(u); @@ -1349,76 +1494,60 @@ int pa__init(pa_module*m) {      if (u->mixer_handle) {          pa_assert(u->mixer_elem); -        if (snd_mixer_selem_has_playback_volume(u->mixer_elem)) - -            if (pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, TRUE) >= 0 && -                snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) >= 0) { - -                pa_bool_t suitable = TRUE; +        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 (u->hw_volume_min > u->hw_volume_max) { - -                    pa_log_info("Minimal volume %li larger than maximum volume %li. Strange stuff Falling back to software volume control.", u->hw_volume_min, u->hw_volume_max); -                    suitable = FALSE; - -                } else if (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; - -                } else if (snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) { - -                    /* u->hw_dB_max = 0; u->hw_dB_min = -3000; Use this to make valgrind shut up */ - -                    pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0); - -                    /* Let's see if this thing actually is useful for muting */ -                    if (u->hw_dB_min > -6000) { -                        pa_log_info("Device cannot attenuate for more than -60 dB (only %0.2f dB supported), falling back to software volume control.", ((double) u->hw_dB_min) / 100); - -                        suitable = FALSE; -                    } else if (u->hw_dB_max < 0) { - -                        pa_log_info("Device is still attenuated at maximum volume setting (%0.2f dB is maximum). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_max) / 100); -                        suitable = FALSE; +            if (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."); +            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 -                    } else if (u->hw_dB_min >= u->hw_dB_max) { +                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; +                } +            } -                        pa_log_info("Minimal dB (%0.2f) larger or equal to maximum dB (%0.2f). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_min) / 100, ((double) u->hw_dB_max) / 100); -                        suitable = FALSE; +            if (suitable && +                !u->hw_dB_supported && +                u->hw_volume_max - u->hw_volume_min < 3) { -                    } else { +                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 (u->hw_dB_max > 0) { -                            /* dB > 0 means overamplification, and clipping, we don't want that here */ -                            pa_log_info("Device can do overamplification for %0.2f dB. Limiting to 0 db", ((double) u->hw_dB_max) / 100); -                            u->hw_dB_max = 0; -                        } +            if (suitable) { +                u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, TRUE) >= 0; -                        u->hw_dB_supported = TRUE; -                    } -                } +                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 (suitable) { -                    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"); - -                } else if (mixer_reset) { -                    pa_log_info("Using software volume control. Trying to reset sound card to 0 dB."); -                    pa_alsa_0dB_playback(u->mixer_elem); -                } else -                    pa_log_info("Using software volume control. Leaving hw mixer controls untouched."); -            } +            } 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(); diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index 1cc467d9..f796ef14 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -28,6 +28,10 @@  #include <asoundlib.h> +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include <valgrind/memcheck.h> +#endif +  #include <pulse/xmalloc.h>  #include <pulse/util.h>  #include <pulse/timeval.h> @@ -69,8 +73,7 @@ PA_MODULE_USAGE(          "mmap=<enable memory mapping?> "          "tsched=<enable system timer based scheduling mode?> "          "tsched_buffer_size=<buffer size when using timer based scheduling> " -        "tsched_buffer_watermark=<upper fill watermark> " -        "mixer_reset=<reset hw volume and mute settings to sane defaults when falling back to software?>"); +        "tsched_buffer_watermark=<upper fill watermark>");  static const char* const valid_modargs[] = {      "source_name", @@ -86,7 +89,6 @@ static const char* const valid_modargs[] = {      "tsched",      "tsched_buffer_size",      "tsched_buffer_watermark", -    "mixer_reset",      NULL  }; @@ -113,6 +115,9 @@ struct userdata {      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_cvolume hardware_volume;      size_t frame_size, fragment_size, hwbuf_size, tsched_watermark;      unsigned nfragments; @@ -136,7 +141,7 @@ static void fix_tsched_watermark(struct userdata *u) {      size_t min_sleep, min_wakeup;      pa_assert(u); -    max_use = u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size; +    max_use = u->hwbuf_size - (size_t) u->hwbuf_unused_frames * u->frame_size;      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); @@ -206,9 +211,10 @@ static int try_recover(struct userdata *u, const char *call, int err) {  static size_t check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) {      size_t left_to_record; +    size_t rec_space = u->hwbuf_size - (size_t) u->hwbuf_unused_frames*u->frame_size; -    if (n*u->frame_size < u->hwbuf_size) -        left_to_record = u->hwbuf_size - (n*u->frame_size); +    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; @@ -232,9 +238,9 @@ static size_t check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) {      return left_to_record;  } -static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) { +static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {      int work_done = 0; -    pa_usec_t max_sleep_usec, process_usec; +    pa_usec_t max_sleep_usec = 0, process_usec = 0;      size_t left_to_record;      pa_assert(u); @@ -249,9 +255,9 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {          snd_pcm_hwsync(u->pcm_handle); -        if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { +        if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) { -            if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0) +            if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)                  continue;              return r; @@ -260,11 +266,20 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {          left_to_record = check_left_to_record(u, n);          if (u->use_tsched) -            if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2) +            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 <= 0)) { + +            if (polled) +                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 PulseAudio device."); +              break; +        } + +        polled = FALSE;          for (;;) {              int err; @@ -272,10 +287,11 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {              snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n;              pa_memchunk chunk;              void *p; +            snd_pcm_sframes_t sframes;  /*             pa_log_debug("%lu frames to read", (unsigned long) frames); */ -            if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { +            if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->source->sample_spec)) < 0)) {                  if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)                      continue; @@ -304,9 +320,9 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {              pa_source_post(u->source, &chunk);              pa_memblock_unref_fixed(chunk.memblock); -            if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { +            if (PA_UNLIKELY((sframes = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { -                if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0) +                if ((r = try_recover(u, "snd_pcm_mmap_commit", (int) sframes)) == 0)                      continue;                  return r; @@ -314,14 +330,14 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {              work_done = 1; -            u->frame_index += frames; +            u->frame_index += (int64_t) frames;  /*             pa_log_debug("read %lu frames", (unsigned long) frames); */              if (frames >= (snd_pcm_uframes_t) n)                  break; -            n -= frames; +            n -= (snd_pcm_sframes_t) frames;          }      } @@ -329,9 +345,9 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {      return work_done;  } -static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) { +static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {      int work_done = 0; -    pa_usec_t max_sleep_usec, process_usec; +    pa_usec_t max_sleep_usec = 0, process_usec = 0;      size_t left_to_record;      pa_assert(u); @@ -346,9 +362,9 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) {          snd_pcm_hwsync(u->pcm_handle); -        if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { +        if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) { -            if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0) +            if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)                  continue;              return r; @@ -357,11 +373,20 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) {          left_to_record = check_left_to_record(u, n);          if (u->use_tsched) -            if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2) +            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 <= 0)) { + +            if (polled) +                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 PulseAudio developers."); +              return work_done; +        } + +        polled = FALSE;          for (;;) {              void *p; @@ -370,7 +395,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) {              chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1); -            frames = pa_memblock_get_length(chunk.memblock) / u->frame_size; +            frames = (snd_pcm_sframes_t) (pa_memblock_get_length(chunk.memblock) / u->frame_size);              if (frames > n)                  frames = n; @@ -378,7 +403,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) {  /*             pa_log_debug("%lu frames to read", (unsigned long) n); */              p = pa_memblock_acquire(chunk.memblock); -            frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, frames); +            frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, (snd_pcm_uframes_t) frames);              pa_memblock_release(chunk.memblock);              pa_assert(frames != 0); @@ -386,14 +411,14 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) {              if (PA_UNLIKELY(frames < 0)) {                  pa_memblock_unref(chunk.memblock); -                if ((r = try_recover(u, "snd_pcm_readi", n)) == 0) +                if ((r = try_recover(u, "snd_pcm_readi", (int) (frames))) == 0)                      continue;                  return r;              }              chunk.index = 0; -            chunk.length = frames * u->frame_size; +            chunk.length = (size_t) frames * u->frame_size;              pa_source_post(u->source, &chunk);              pa_memblock_unref(chunk.memblock); @@ -437,7 +462,7 @@ static void update_smoother(struct userdata *u) {      frames = u->frame_index + delay;      now1 = pa_rtclock_usec(); -    now2 = pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec); +    now2 = pa_bytes_to_usec((uint64_t) frames * u->frame_size, &u->source->sample_spec);      pa_smoother_put(u->smoother, now1, now2);  } @@ -452,7 +477,7 @@ 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 - pa_bytes_to_usec(u->frame_index * u->frame_size, &u->source->sample_spec); +    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; @@ -508,7 +533,7 @@ static int update_sw_params(struct userdata *u) {          if ((latency = pa_source_get_requested_latency_within_thread(u->source)) != (pa_usec_t) -1) {              size_t b; -            pa_log_debug("latency set to %0.2f", (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->source->sample_spec); @@ -517,9 +542,9 @@ static int update_sw_params(struct userdata *u) {              if (PA_UNLIKELY(b < u->frame_size))                  b = u->frame_size; -            u->hwbuf_unused_frames = -                PA_LIKELY(b < u->hwbuf_size) ? -                ((u->hwbuf_size - b) / u->frame_size) : 0; +            u->hwbuf_unused_frames = (snd_pcm_sframes_t) +                (PA_LIKELY(b < u->hwbuf_size) ? +                 ((u->hwbuf_size - b) / u->frame_size) : 0);              fix_tsched_watermark(u);          } @@ -559,7 +584,12 @@ static int unsuspend(struct userdata *u) {      pa_log_info("Trying resume...");      snd_config_update_free_global(); -    if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { + +    if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE, +                            /*SND_PCM_NONBLOCK|*/ +                            SND_PCM_NO_AUTO_RESAMPLE| +                            SND_PCM_NO_AUTO_CHANNELS| +                            SND_PCM_NO_AUTO_FORMAT)) < 0) {          pa_log("Error opening PCM device %s: %s", u->device_name, snd_strerror(err));          goto fail;      } @@ -586,7 +616,9 @@ static int unsuspend(struct userdata *u) {      }      if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) { -        pa_log_warn("Resume failed, couldn't restore original fragment settings."); +        pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu*%lu, New %lu*%lu)", +                    (unsigned long) u->nfragments, (unsigned long) u->fragment_size, +                    (unsigned long) nfrags, period_size * u->frame_size);          goto fail;      } @@ -680,40 +712,101 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {          return 0;      if (mask & SND_CTL_EVENT_MASK_VALUE) { -        pa_source_get_volume(u->source); -        pa_source_get_mute(u->source); +        pa_source_get_volume(u->source, TRUE); +        pa_source_get_mute(u->source, TRUE);      }      return 0;  } +static pa_volume_t from_alsa_volume(struct userdata *u, long alsa_vol) { + +    return (pa_volume_t) round(((double) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / +                               (double) (u->hw_volume_max - u->hw_volume_min)); +} + +static long to_alsa_volume(struct userdata *u, pa_volume_t vol) { +    long alsa_vol; + +    alsa_vol = (long) round(((double) vol * (double) (u->hw_volume_max - u->hw_volume_min)) +                            / PA_VOLUME_NORM) + u->hw_volume_min; + +    return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); +} +  static int source_get_volume_cb(pa_source *s) {      struct userdata *u = s->userdata;      int err; -    int i; +    unsigned i; +    pa_cvolume r; +    char t[PA_CVOLUME_SNPRINT_MAX];      pa_assert(u);      pa_assert(u->mixer_elem); -    for (i = 0; i < s->sample_spec.channels; i++) { -        long alsa_vol; +    if (u->mixer_seperate_channels) { -        pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, u->mixer_map[i])); +        r.channels = s->sample_spec.channels; -        if (u->hw_dB_supported) { +        for (i = 0; i < s->sample_spec.channels; i++) { +            long alsa_vol; -            if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) >= 0) { -                s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0); -                continue; +            if (u->hw_dB_supported) { + +                if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) +                    goto fail; + +#ifdef HAVE_VALGRIND_MEMCHECK_H +                VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); +#endif + +                r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0); +            } else { + +                if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) +                    goto fail; + +                r.values[i] = from_alsa_volume(u, alsa_vol);              } +        } + +    } else { +        long alsa_vol; + +        if (u->hw_dB_supported) { + +            if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) +                goto fail; + +#ifdef HAVE_VALGRIND_MEMCHECK_H +            VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); +#endif + +            pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0)); + +        } else { -            u->hw_dB_supported = FALSE; +            if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) +                goto fail; + +            pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol));          } +    } -        if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) -            goto fail; +    pa_log_debug("Read hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); + +    if (!pa_cvolume_equal(&u->hardware_volume, &r)) { + +        u->hardware_volume = s->volume = r; + +        if (u->hw_dB_supported) { +            pa_cvolume reset; + +            /* Hmm, so the hardware volume changed, let's reset our software volume */ -        s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); +            pa_cvolume_reset(&reset, s->sample_spec.channels); +            pa_source_set_soft_volume(s, &reset); +        }      }      return 0; @@ -727,44 +820,101 @@ fail:  static int source_set_volume_cb(pa_source *s) {      struct userdata *u = s->userdata;      int err; -    int i; +    unsigned i; +    pa_cvolume r;      pa_assert(u);      pa_assert(u->mixer_elem); -    for (i = 0; i < s->sample_spec.channels; i++) { -        long alsa_vol; -        pa_volume_t vol; +    if (u->mixer_seperate_channels) { + +        r.channels = s->sample_spec.channels; + +        for (i = 0; i < s->sample_spec.channels; i++) { +            long alsa_vol; +            pa_volume_t vol; -        pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, u->mixer_map[i])); +            vol = s->volume.values[i]; -        vol = PA_MIN(s->volume.values[i], PA_VOLUME_NORM); +            if (u->hw_dB_supported) { + +                alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); +                alsa_vol += u->hw_dB_max; +                alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); + +                if ((err = snd_mixer_selem_set_capture_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, 1)) < 0) +                    goto fail; + +                if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) +                    goto fail; + +                r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0); + +            } else { +                alsa_vol = to_alsa_volume(u, vol); + +                if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0) +                    goto fail; + +                if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) +                    goto fail; + +                r.values[i] = from_alsa_volume(u, alsa_vol); +            } +        } + +    } else { +        pa_volume_t vol; +        long alsa_vol; + +        vol = pa_cvolume_max(&s->volume);          if (u->hw_dB_supported) {              alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); +            alsa_vol += u->hw_dB_max;              alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); +            if ((err = snd_mixer_selem_set_capture_dB_all(u->mixer_elem, alsa_vol, 1)) < 0) +                goto fail; -            if ((err = snd_mixer_selem_set_capture_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, -1)) >= 0) { +            if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) +                goto fail; -                if (snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0) -                    s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0); +            pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0)); -                continue; -            } +        } else { +            alsa_vol = to_alsa_volume(u, vol); + +            if ((err = snd_mixer_selem_set_capture_volume_all(u->mixer_elem, alsa_vol)) < 0) +                goto fail; -            u->hw_dB_supported = FALSE; +            if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) +                goto fail; + +            pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol));          } +    } -        alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; -        alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); +    u->hardware_volume = r; -        if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0) -            goto fail; +    if (u->hw_dB_supported) { +        char t[PA_CVOLUME_SNPRINT_MAX]; -        if (snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0) -            s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); -    } +        /* Match exactly what the user requested by software */ + +        pa_sw_cvolume_divide(&r, &s->volume, &r); +        pa_source_set_soft_volume(s, &r); + +        pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume)); +        pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); +        pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); + +    } else + +        /* We can't match exactly what the user requested, hence let's +         * at least tell the user about it */ + +        s->volume = r;      return 0; @@ -818,6 +968,7 @@ static void source_update_requested_latency_cb(pa_source *s) {  static void thread_func(void *userdata) {      struct userdata *u = userdata; +    unsigned short revents = 0;      pa_assert(u); @@ -837,12 +988,12 @@ static void thread_func(void *userdata) {          /* Read some data and pass it to the sources */          if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {              int work_done = 0; -            pa_usec_t sleep_usec; +            pa_usec_t sleep_usec = 0;              if (u->use_mmap) -                work_done = mmap_read(u, &sleep_usec); +                work_done = mmap_read(u, &sleep_usec, revents & POLLIN);              else -                work_done = unix_read(u, &sleep_usec); +                work_done = unix_read(u, &sleep_usec, revents & POLLIN);              if (work_done < 0)                  goto fail; @@ -875,7 +1026,7 @@ static void thread_func(void *userdata) {              pa_rtpoll_set_timer_disabled(u->rtpoll);          /* Hmm, nothing to do. Let's sleep */ -        if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0) +        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)              goto fail;          if (ret == 0) @@ -884,7 +1035,6 @@ static void thread_func(void *userdata) {          /* Tell ALSA about this and process its response */          if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {              struct pollfd *pollfd; -            unsigned short revents = 0;              int err;              unsigned n; @@ -895,7 +1045,7 @@ static void thread_func(void *userdata) {                  goto fail;              } -            if (revents & (POLLERR|POLLNVAL|POLLHUP)) { +            if (revents & (POLLOUT|POLLERR|POLLNVAL|POLLHUP|POLLPRI)) {                  if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)                      goto fail; @@ -903,8 +1053,9 @@ static void thread_func(void *userdata) {              }              if (revents && u->use_tsched) -                pa_log_debug("Wakeup from ALSA! (%i)", revents); -        } +                pa_log_debug("Wakeup from ALSA!%s%s", (revents & POLLIN) ? " INPUT" : "", (revents & POLLOUT) ? " OUTPUT" : ""); +        } else +            revents = 0;      }  fail: @@ -932,7 +1083,7 @@ int pa__init(pa_module*m) {      const char *name;      char *name_buf = NULL;      pa_bool_t namereg_fail; -    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, mixer_reset = TRUE; +    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;      pa_source_new_data data;      snd_pcm_info_alloca(&pcm_info); @@ -955,11 +1106,11 @@ int pa__init(pa_module*m) {      frame_size = pa_frame_size(&ss);      nfrags = m->core->default_n_fragments; -    frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss); +    frag_size = (uint32_t) pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss);      if (frag_size <= 0) -        frag_size = frame_size; -    tsched_size = pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss); -    tsched_watermark = pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss); +        frag_size = (uint32_t) frame_size; +    tsched_size = (uint32_t) pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss); +    tsched_watermark = (uint32_t) pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss);      if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 ||          pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 || @@ -984,15 +1135,10 @@ int pa__init(pa_module*m) {      }      if (use_tsched && !pa_rtclock_hrtimer()) { -        pa_log("Disabling timer-based scheduling because high-resolution timers are not available from the kernel."); +        pa_log_notice("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");          use_tsched = FALSE;      } -    if (pa_modargs_get_value_boolean(ma, "mixer_reset", &mixer_reset) < 0) { -        pa_log("Failed to parse mixer_reset argument."); -        goto fail; -    } -      u = pa_xnew0(struct userdata, 1);      u->core = m->core;      u->module = m; @@ -1137,7 +1283,7 @@ int pa__init(pa_module*m) {      pa_source_set_rtpoll(u->source, u->rtpoll);      u->frame_size = frame_size; -    u->fragment_size = frag_size = period_frames * frame_size; +    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; @@ -1146,6 +1292,8 @@ int pa__init(pa_module*m) {      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)          fix_tsched_watermark(u); @@ -1168,67 +1316,59 @@ int pa__init(pa_module*m) {      if (u->mixer_handle) {          pa_assert(u->mixer_elem); -        if (snd_mixer_selem_has_capture_volume(u->mixer_elem)) -            if (pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, FALSE) >= 0 && -                snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) >= 0) { - -                pa_bool_t suitable = TRUE; +        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 (u->hw_volume_min > u->hw_volume_max) { - -                    pa_log_info("Minimal volume %li larger than maximum volume %li. Strange stuff Falling back to software volume control.", u->hw_volume_min, u->hw_volume_max); -                    suitable = FALSE; - -                } else if (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; - -                } else if (snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) { - -                    pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0); - -                    /* Let's see if this thing actually is useful for muting */ -                    if (u->hw_dB_min > -6000) { -                        pa_log_info("Device cannot attenuate for more than -60 dB (only %0.2f dB supported), falling back to software volume control.", ((double) u->hw_dB_min) / 100); - -                        suitable = FALSE; -                    } else if (u->hw_dB_max < 0) { - -                        pa_log_info("Device is still attenuated at maximum volume setting (%0.2f dB is maximum). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_max) / 100); -                        suitable = FALSE; - -                    } else if (u->hw_dB_min >= u->hw_dB_max) { - -                        pa_log_info("Minimal dB (%0.2f) larger or equal to maximum dB (%0.2f). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_min) / 100, ((double) u->hw_dB_max) / 100); -                        suitable = FALSE; +            if (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."); +            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 -                    } else -                        u->hw_dB_supported = TRUE; +                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 (suitable) { -                    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"); - -                } else if (mixer_reset) { -                    pa_log_info("Using software volume control. Trying to reset sound card to 0 dB."); -                    pa_alsa_0dB_capture(u->mixer_elem); -                } else -                    pa_log_info("Using software volume control. Leaving hw mixer controls untouched."); +            if (suitable && +                !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"); +            } 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(); diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 1bfe4b4c..d61d127a 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -206,9 +206,9 @@ static void adjust_rates(struct userdata *u) {              continue;          if (o->total_latency < target_latency) -            r -= (uint32_t) (((((double) target_latency - o->total_latency))/u->adjust_time)*r/PA_USEC_PER_SEC); +            r -= (uint32_t) ((((double) (target_latency - o->total_latency))/(double)u->adjust_time)*(double)r/PA_USEC_PER_SEC);          else if (o->total_latency > target_latency) -            r += (uint32_t) (((((double) o->total_latency - target_latency))/u->adjust_time)*r/PA_USEC_PER_SEC); +            r += (uint32_t) ((((double) (o->total_latency - target_latency))/(double)u->adjust_time)*(double)r/PA_USEC_PER_SEC);          if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1)) {              pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), base_rate, r); @@ -233,7 +233,7 @@ static void time_callback(pa_mainloop_api*a, pa_time_event* e, const struct time      adjust_rates(u);      pa_gettimeofday(&n); -    n.tv_sec += u->adjust_time; +    n.tv_sec += (time_t) u->adjust_time;      u->sink->core->mainloop->time_restart(e, &n);  } @@ -389,7 +389,7 @@ static void request_memblock(struct output *o, size_t length) {      /* OK, we need to prepare new data, but only if the sink is actually running */      if (pa_atomic_load(&o->userdata->thread_info.running)) -        pa_asyncmsgq_send(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_NEED, o, length, NULL); +        pa_asyncmsgq_send(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_NEED, o, (int64_t) length, NULL);  }  /* Called from I/O thread context */ @@ -1159,7 +1159,7 @@ int pa__init(pa_module*m) {      if (u->adjust_time > 0) {          struct timeval tv;          pa_gettimeofday(&tv); -        tv.tv_sec += u->adjust_time; +        tv.tv_sec += (time_t) u->adjust_time;          u->time_event = m->core->mainloop->time_new(m->core->mainloop, &tv, time_callback, u);      } diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c index 09b720df..1616d47c 100644 --- a/src/modules/module-detect.c +++ b/src/modules/module-detect.c @@ -236,16 +236,16 @@ int pa__init(pa_module*m) {          goto fail;      } -#if HAVE_ALSA +#ifdef HAVE_ALSA      if ((n = detect_alsa(m->core, just_one)) <= 0)  #endif  #if HAVE_OSS      if ((n = detect_oss(m->core, just_one)) <= 0)  #endif -#if HAVE_SOLARIS +#ifdef HAVE_SOLARIS      if ((n = detect_solaris(m->core, just_one)) <= 0)  #endif -#if OS_IS_WIN32 +#ifdef OS_IS_WIN32      if ((n = detect_waveout(m->core, just_one)) <= 0)  #endif      { diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c index 3d731f12..86a78810 100644 --- a/src/modules/module-device-restore.c +++ b/src/modules/module-device-restore.c @@ -106,7 +106,7 @@ static struct entry* read_entry(struct userdata *u, char *name) {      pa_assert(name);      key.dptr = name; -    key.dsize = strlen(name); +    key.dsize = (int) strlen(name);      data = gdbm_fetch(u->gdbm_file, key); @@ -179,8 +179,8 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3          name = pa_sprintf_malloc("sink:%s", sink->name);          entry.channel_map = sink->channel_map; -        entry.volume = *pa_sink_get_volume(sink); -        entry.muted = pa_sink_get_mute(sink); +        entry.volume = *pa_sink_get_volume(sink, FALSE); +        entry.muted = pa_sink_get_mute(sink, FALSE);      } else {          pa_source *source; @@ -192,8 +192,8 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3          name = pa_sprintf_malloc("source:%s", source->name);          entry.channel_map = source->channel_map; -        entry.volume = *pa_source_get_volume(source); -        entry.muted = pa_source_get_mute(source); +        entry.volume = *pa_source_get_volume(source, FALSE); +        entry.muted = pa_source_get_mute(source, FALSE);      }      if ((old = read_entry(u, name))) { @@ -210,7 +210,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3      }      key.dptr = name; -    key.dsize = strlen(name); +    key.dsize = (int) strlen(name);      data.dptr = (void*) &entry;      data.dsize = sizeof(entry); @@ -288,6 +288,7 @@ int pa__init(pa_module*m) {      pa_source *source;      uint32_t idx;      pa_bool_t restore_volume = TRUE, restore_muted = TRUE; +    int gdbm_cache_size;      pa_assert(m); @@ -337,6 +338,10 @@ int pa__init(pa_module*m) {          goto fail;      } +    /* By default the cache of gdbm is rather large, let's reduce it a bit to save memory */ +    gdbm_cache_size = 10; +    gdbm_setopt(u->gdbm_file, GDBM_CACHESIZE, &gdbm_cache_size, sizeof(gdbm_cache_size)); +      pa_log_info("Sucessfully opened database file '%s'.", fname);      pa_xfree(fname); diff --git a/src/modules/module-esound-compat-spawnpid.c b/src/modules/module-esound-compat-spawnpid.c index f75335d5..882dba8c 100644 --- a/src/modules/module-esound-compat-spawnpid.c +++ b/src/modules/module-esound-compat-spawnpid.c @@ -62,7 +62,7 @@ int pa__init(pa_module*m) {          goto finish;      } -    if (kill(pid, SIGUSR1) < 0) +    if (kill((pid_t) pid, SIGUSR1) < 0)          pa_log_warn("kill(%u) failed: %s", pid, pa_cstrerror(errno));      pa_module_unload_request(m, TRUE); diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index f748808e..14f1810a 100644 --- a/src/modules/module-esound-sink.c +++ b/src/modules/module-esound-sink.c @@ -165,7 +165,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse              pa_usec_t w, r;              r = pa_smoother_get(u->smoother, pa_rtclock_usec()); -            w = pa_bytes_to_usec(u->offset + u->memchunk.length, &u->sink->sample_spec); +            w = pa_bytes_to_usec((uint64_t) u->offset + u->memchunk.length, &u->sink->sample_spec);              *((pa_usec_t*) data) = w > r ? w - r : 0;              break; @@ -250,8 +250,8 @@ static void thread_func(void *userdata) {                      } else {                          u->offset += l; -                        u->memchunk.index += l; -                        u->memchunk.length -= l; +                        u->memchunk.index += (size_t) l; +                        u->memchunk.length -= (size_t) l;                          if (u->memchunk.length <= 0) {                              pa_memblock_unref(u->memchunk.memblock); @@ -285,7 +285,7 @@ static void thread_func(void *userdata) {                  }  #endif -                usec = pa_bytes_to_usec(n, &u->sink->sample_spec); +                usec = pa_bytes_to_usec((uint64_t) n, &u->sink->sample_spec);                  if (usec > u->latency)                      usec -= u->latency; @@ -296,7 +296,7 @@ static void thread_func(void *userdata) {              }              /* Hmm, nothing to do. Let's sleep */ -            pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0; +            pollfd->events = (short) (PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0);          }          if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) @@ -342,7 +342,7 @@ static int do_write(struct userdata *u) {              return -1;          } -        u->write_index += r; +        u->write_index += (size_t) r;          pa_assert(u->write_index <= u->write_length);          if (u->write_index == u->write_length) { @@ -458,7 +458,7 @@ static int do_read(struct userdata *u) {              return -1;          } -        u->read_index += r; +        u->read_index += (size_t) r;          pa_assert(u->read_index <= u->read_length);          if (u->read_index == u->read_length) @@ -468,7 +468,7 @@ static int do_read(struct userdata *u) {      return 0;  } -static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { +static void io_callback(pa_iochannel *io, void*userdata) {      struct userdata *u = userdata;      pa_assert(u); @@ -483,7 +483,7 @@ static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {      }  } -static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, void *userdata) { +static void on_connection(pa_socket_client *c, pa_iochannel*io, void *userdata) {      struct userdata *u = userdata;      pa_socket_client_unref(u->client); @@ -545,7 +545,7 @@ int pa__init(pa_module*m) {      u->format =          (ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) |          (ss.channels == 2 ? ESD_STEREO : ESD_MONO); -    u->rate = ss.rate; +    u->rate = (int32_t) ss.rate;      u->block_size = pa_usec_to_bytes(PA_USEC_PER_SEC/20, &ss);      u->read_data = u->write_data = NULL; diff --git a/src/modules/module-flat-volume.c b/src/modules/module-flat-volume.c new file mode 100644 index 00000000..9bc8055a --- /dev/null +++ b/src/modules/module-flat-volume.c @@ -0,0 +1,224 @@ +/*** +  This file is part of PulseAudio. + +  Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +  Copyright 2004-2006, 2008 Lennart Poettering + +  Contact: Marc-Andre Lureau <marc-andre.lureau@nokia.com> + +  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 <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/module.h> +#include <pulsecore/core-util.h> +#include <pulsecore/log.h> +#include <pulsecore/sink-input.h> +#include <pulsecore/core-util.h> +#include <pulsecore/macro.h> + +#include "module-flat-volume-symdef.h" + +PA_MODULE_AUTHOR("Marc-Andre Lureau"); +PA_MODULE_DESCRIPTION("Flat volume"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_USAGE(""); + +struct userdata { +    pa_subscription *subscription; +    pa_hook_slot *sink_input_set_volume_hook_slot; +    pa_hook_slot *sink_input_fixate_hook_slot; +}; + +static void process_input_volume_change( +        pa_cvolume *dest_volume, +        const pa_cvolume *dest_virtual_volume, +        pa_channel_map *dest_channel_map, +        pa_sink_input *this, +        pa_sink *sink) { + +    pa_sink_input *i; +    uint32_t idx; +    pa_cvolume max_volume, sink_volume; + +    pa_assert(dest_volume); +    pa_assert(dest_virtual_volume); +    pa_assert(dest_channel_map); +    pa_assert(sink); + +    if (!(sink->flags & PA_SINK_DECIBEL_VOLUME)) +        return; + +    pa_log_debug("Sink input volume changed"); + +    max_volume = *dest_virtual_volume; +    pa_cvolume_remap(&max_volume, dest_channel_map, &sink->channel_map); + +    for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &idx))) { +        /* skip this sink-input if we are processing a volume change request */ +        if (this && this == i) +            continue; + +        if (pa_cvolume_max(&i->virtual_volume) > pa_cvolume_max(&max_volume)) { +            max_volume = i->virtual_volume; +            pa_cvolume_remap(&max_volume, &i->channel_map, &sink->channel_map); +        } +    } + +    /* Set the master volume, and normalize inputs */ +    if (!pa_cvolume_equal(&max_volume, &sink->volume)) { + +        pa_sink_set_volume(sink, &max_volume); + +        pa_log_debug("sink = %.2f (changed)", (double)pa_cvolume_avg(&sink->volume)/PA_VOLUME_NORM); + +        /* Now, normalize each of the internal volume (client sink-input volume / sink master volume) */ +        for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &idx))) { +            /* skip this sink-input if we are processing a volume change request */ +            if (this && this == i) +                continue; + +            sink_volume = max_volume; +            pa_cvolume_remap(&sink_volume, &sink->channel_map, &i->channel_map); +            pa_sw_cvolume_divide(&i->volume, &i->virtual_volume, &sink_volume); +            pa_log_debug("sink input { id = %d, flat = %.2f, true = %.2f }", +                         i->index, +                         (double)pa_cvolume_avg(&i->virtual_volume)/PA_VOLUME_NORM, +                         (double)pa_cvolume_avg(&i->volume)/PA_VOLUME_NORM); +            pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, &i->volume, 1), 0, NULL, pa_xfree); +        } +    } else +        pa_log_debug("sink = %.2f", (double)pa_cvolume_avg(&sink->volume)/PA_VOLUME_NORM); + +    /* and this one */ + +    sink_volume = max_volume; +    pa_cvolume_remap(&sink_volume, &sink->channel_map, dest_channel_map); +    pa_sw_cvolume_divide(dest_volume, dest_virtual_volume, &sink_volume); +    pa_log_debug("caller sink input: { id = %d, flat = %.2f, true = %.2f }", +                 this ? (int)this->index : -1, +                 (double)pa_cvolume_avg(dest_virtual_volume)/PA_VOLUME_NORM, +                 (double)pa_cvolume_avg(dest_volume)/PA_VOLUME_NORM); +} + +static pa_hook_result_t sink_input_set_volume_hook_callback(pa_core *c, pa_sink_input_set_volume_data *this, struct userdata *u) { +    pa_assert(this); +    pa_assert(this->sink_input); + +    process_input_volume_change(&this->volume, &this->virtual_volume, &this->sink_input->channel_map, +                                this->sink_input, this->sink_input->sink); + +    return PA_HOOK_OK; +} + +static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *this, struct userdata *u) { +    pa_assert(this); +    pa_assert(this->sink); + +    process_input_volume_change(&this->volume, &this->virtual_volume, &this->channel_map, +                                NULL, this->sink); + +    return PA_HOOK_OK; +} + +static void subscribe_callback(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { +    struct userdata *u = userdata; +    pa_sink *sink; +    pa_sink_input *i; +    uint32_t iidx; +    pa_cvolume sink_volume; + +    pa_assert(core); +    pa_assert(u); + +    if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) && +        t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE)) +        return; + +    if (!(sink = pa_idxset_get_by_index(core->sinks, idx))) +        return; + +    if (!(sink->flags & PA_SINK_DECIBEL_VOLUME)) +        return; + +    pa_log_debug("Sink volume changed"); +    pa_log_debug("sink = %.2f", (double)pa_cvolume_avg(pa_sink_get_volume(sink, FALSE)) / PA_VOLUME_NORM); + +    sink_volume = *pa_sink_get_volume(sink, FALSE); + +    for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &iidx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &iidx))) { +        pa_cvolume si_volume; + +        si_volume = sink_volume; +        pa_cvolume_remap(&si_volume, &sink->channel_map, &i->channel_map); +        pa_sw_cvolume_multiply(&i->virtual_volume, &i->volume, &si_volume); +        pa_log_debug("sink input = { id = %d, flat = %.2f, true = %.2f }", +                     i->index, +                     (double)pa_cvolume_avg(&i->virtual_volume)/PA_VOLUME_NORM, +                     (double)pa_cvolume_avg(&i->volume)/PA_VOLUME_NORM); +        pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +    } +} + +int pa__init(pa_module*m) { +    struct userdata *u; + +    pa_assert(m); + +    u = pa_xnew(struct userdata, 1); +    m->userdata = u; + +    u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_fixate_hook_callback, u); +    u->sink_input_set_volume_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_set_volume_hook_callback, u); + +    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK, subscribe_callback, u); + +    return 0; +} + +void pa__done(pa_module*m) { +    struct userdata* u; + +    pa_assert(m); + +    if (!(u = m->userdata)) +        return; + +    if (u->subscription) +      pa_subscription_free(u->subscription); + +    if (u->sink_input_set_volume_hook_slot) +        pa_hook_slot_free(u->sink_input_set_volume_hook_slot); +    if (u->sink_input_fixate_hook_slot) +        pa_hook_slot_free(u->sink_input_fixate_hook_slot); + +    pa_xfree(u); +} diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c index caa7a1fa..c76a366c 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -108,7 +108,7 @@ static void hal_device_free(struct device* d) {      pa_xfree(d);  } -static void hal_device_free_cb(void *d, PA_GCC_UNUSED void *data) { +static void hal_device_free_cb(void *d, void *data) {      hal_device_free(d);  } @@ -405,7 +405,7 @@ static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev, const s      dbus_error_init(&error);      if (!pa_hashmap_get(td->u->devices, td->udi)) { -        int b; +        dbus_bool_t b;          struct device *d;          b = libhal_device_exists(td->u->context, td->udi, &error); @@ -433,7 +433,7 @@ static void device_added_cb(LibHalContext *context, const char *udi) {      struct timeval tv;      struct timerdata *t;      struct userdata *u; -    int good = 0; +    pa_bool_t good = FALSE;      pa_assert_se(u = libhal_ctx_get_user_data(context)); @@ -749,17 +749,17 @@ int pa__init(pa_module*m) {      }      if ((api = pa_modargs_get_value(ma, "api", NULL))) { -        int good = 0; +        pa_bool_t good = FALSE;  #ifdef HAVE_ALSA          if (strcmp(api, CAPABILITY_ALSA) == 0) { -            good = 1; +            good = TRUE;              api = CAPABILITY_ALSA;          }  #endif  #ifdef HAVE_OSS          if (strcmp(api, CAPABILITY_OSS) == 0) { -            good = 1; +            good = TRUE;              api = CAPABILITY_OSS;          }  #endif diff --git a/src/modules/module-jack-sink.c b/src/modules/module-jack-sink.c index edc543a8..555cb825 100644 --- a/src/modules/module-jack-sink.c +++ b/src/modules/module-jack-sink.c @@ -130,12 +130,12 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse                  void *p;                  pa_assert(offset > 0); -                nbytes = offset * pa_frame_size(&u->sink->sample_spec); +                nbytes = (size_t) offset * pa_frame_size(&u->sink->sample_spec);                  pa_sink_render_full(u->sink, nbytes, &chunk);                  p = (uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index; -                pa_deinterleave(p, u->buffer, u->channels, sizeof(float), offset); +                pa_deinterleave(p, u->buffer, u->channels, sizeof(float), (unsigned) offset);                  pa_memblock_release(chunk.memblock);                  pa_memblock_unref(chunk.memblock); @@ -149,10 +149,10 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse                  ss.channels = 1;                  for (c = 0; c < u->channels; c++) -                    pa_silence_memory(u->buffer[c], offset * pa_sample_size(&ss), &ss); +                    pa_silence_memory(u->buffer[c], (size_t) offset * pa_sample_size(&ss), &ss);              } -            u->frames_in_buffer = offset; +            u->frames_in_buffer = (jack_nframes_t) offset;              u->saved_frame_time = * (jack_nframes_t*) data;              u->saved_frame_time_valid = TRUE; @@ -342,7 +342,7 @@ int pa__init(pa_module*m) {      pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client)); -    ss.channels = u->channels = channels; +    u->channels = ss.channels = (uint8_t) channels;      ss.rate = jack_get_sample_rate(u->client);      ss.format = PA_SAMPLE_FLOAT32NE; diff --git a/src/modules/module-jack-source.c b/src/modules/module-jack-source.c index 03f9d15c..9eccbbfa 100644 --- a/src/modules/module-jack-source.c +++ b/src/modules/module-jack-source.c @@ -116,7 +116,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off              if (u->source->thread_info.state == PA_SOURCE_RUNNING)                  pa_source_post(u->source, chunk); -            u->saved_frame_time = offset; +            u->saved_frame_time = (jack_nframes_t) offset;              u->saved_frame_time_valid = TRUE;              return 0; @@ -309,7 +309,7 @@ int pa__init(pa_module*m) {      pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client)); -    ss.channels = u->channels = channels; +    u->channels = ss.channels = (uint8_t) channels;      ss.rate = jack_get_sample_rate(u->client);      ss.format = PA_SAMPLE_FLOAT32NE; diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 23eeb340..a27ed712 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -187,7 +187,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk      pa_assert(tchunk.length > 0);      fs = pa_frame_size(&i->sample_spec); -    n = PA_MIN(tchunk.length, u->block_size) / fs; +    n = (unsigned) (PA_MIN(tchunk.length, u->block_size) / fs);      pa_assert(n > 0); @@ -359,6 +359,16 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s      }  } +/* Called from main context */ +static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { +    struct userdata *u; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(u = i->userdata); + +    return u->sink != dest; +} +  int pa__init(pa_module*m) {      struct userdata *u;      pa_sample_spec ss; @@ -502,9 +512,9 @@ int pa__init(pa_module*m) {      u->block_size = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss); -    u->input = (LADSPA_Data*) pa_xnew(uint8_t, u->block_size); +    u->input = (LADSPA_Data*) pa_xnew(uint8_t, (unsigned) u->block_size);      if (LADSPA_IS_INPLACE_BROKEN(d->Properties)) -        u->output = (LADSPA_Data*) pa_xnew(uint8_t, u->block_size); +        u->output = (LADSPA_Data*) pa_xnew(uint8_t, (unsigned) u->block_size);      else          u->output = u->input; @@ -530,8 +540,8 @@ int pa__init(pa_module*m) {          char *k;          unsigned long h; -        u->control = pa_xnew(LADSPA_Data, n_control); -        use_default = pa_xnew(pa_bool_t, n_control); +        u->control = pa_xnew(LADSPA_Data, (unsigned) n_control); +        use_default = pa_xnew(pa_bool_t, (unsigned) n_control);          p = 0;          while ((k = pa_split(cdata, ",", &state)) && p < n_control) { @@ -552,7 +562,7 @@ int pa__init(pa_module*m) {              pa_xfree(k);              use_default[p] = FALSE; -            u->control[p++] = f; +            u->control[p++] = (LADSPA_Data) f;          }          /* The previous loop doesn't take the last control value into account @@ -601,8 +611,8 @@ int pa__init(pa_module*m) {                  upper = d->PortRangeHints[p].UpperBound;                  if (LADSPA_IS_HINT_SAMPLE_RATE(hint)) { -                    lower *= ss.rate; -                    upper *= ss.rate; +                    lower *= (LADSPA_Data) ss.rate; +                    upper *= (LADSPA_Data) ss.rate;                  }                  switch (hint & LADSPA_HINT_DEFAULT_MASK) { @@ -617,23 +627,23 @@ int pa__init(pa_module*m) {                      case LADSPA_HINT_DEFAULT_LOW:                          if (LADSPA_IS_HINT_LOGARITHMIC(hint)) -                            u->control[h] = exp(log(lower) * 0.75 + log(upper) * 0.25); +                            u->control[h] = (LADSPA_Data) exp(log(lower) * 0.75 + log(upper) * 0.25);                          else -                            u->control[h] = lower * 0.75 + upper * 0.25; +                            u->control[h] = (LADSPA_Data) (lower * 0.75 + upper * 0.25);                          break;                      case LADSPA_HINT_DEFAULT_MIDDLE:                          if (LADSPA_IS_HINT_LOGARITHMIC(hint)) -                            u->control[h] = exp(log(lower) * 0.5 + log(upper) * 0.5); +                            u->control[h] = (LADSPA_Data) exp(log(lower) * 0.5 + log(upper) * 0.5);                          else -                            u->control[h] = lower * 0.5 + upper * 0.5; +                            u->control[h] = (LADSPA_Data) (lower * 0.5 + upper * 0.5);                          break;                      case LADSPA_HINT_DEFAULT_HIGH:                          if (LADSPA_IS_HINT_LOGARITHMIC(hint)) -                            u->control[h] = exp(log(lower) * 0.25 + log(upper) * 0.75); +                            u->control[h] = (LADSPA_Data) exp(log(lower) * 0.25 + log(upper) * 0.75);                          else -                            u->control[h] = lower * 0.25 + upper * 0.75; +                            u->control[h] = (LADSPA_Data) (lower * 0.25 + upper * 0.75);                          break;                      case LADSPA_HINT_DEFAULT_0: @@ -737,6 +747,7 @@ int pa__init(pa_module*m) {      u->sink_input->attach = sink_input_attach_cb;      u->sink_input->detach = sink_input_detach_cb;      u->sink_input->state_change = sink_input_state_change_cb; +    u->sink_input->may_move_to = sink_input_may_move_to_cb;      u->sink_input->userdata = u;      pa_sink_put(u->sink); diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c index f34f7be3..97e97dc7 100644 --- a/src/modules/module-lirc.c +++ b/src/modules/module-lirc.c @@ -65,7 +65,7 @@ struct userdata {  static int lirc_in_use = 0; -static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) { +static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void*userdata) {      struct userdata *u = userdata;      char *name = NULL, *code = NULL; @@ -122,7 +122,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC                      pa_log("Failed to get sink '%s'", u->sink_name);                  else {                      int i; -                    pa_cvolume cv = *pa_sink_get_volume(s); +                    pa_cvolume cv = *pa_sink_get_volume(s, FALSE);  #define DELTA (PA_VOLUME_NORM/20) @@ -159,7 +159,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC                          case MUTE_TOGGLE: -                            pa_sink_set_mute(s, !pa_sink_get_mute(s)); +                            pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE));                              break;                          case INVALID: diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c index 7da77c0d..21f176a4 100644 --- a/src/modules/module-mmkbd-evdev.c +++ b/src/modules/module-mmkbd-evdev.c @@ -76,7 +76,7 @@ struct userdata {      pa_module *module;  }; -static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) { +static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void*userdata) {      struct userdata *u = userdata;      pa_assert(io); @@ -113,7 +113,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC                      pa_log("Failed to get sink '%s'", u->sink_name);                  else {                      int i; -                    pa_cvolume cv = *pa_sink_get_volume(s); +                    pa_cvolume cv = *pa_sink_get_volume(s, FALSE);  #define DELTA (PA_VOLUME_NORM/20) @@ -142,7 +142,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC                          case MUTE_TOGGLE: -                            pa_sink_set_mute(s, !pa_sink_get_mute(s)); +                            pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE));                              break;                          case INVALID: diff --git a/src/modules/module-native-protocol-fd.c b/src/modules/module-native-protocol-fd.c index fa9c0e4f..f17f435a 100644 --- a/src/modules/module-native-protocol-fd.c +++ b/src/modules/module-native-protocol-fd.c @@ -48,7 +48,8 @@ static const char* const valid_modargs[] = {  int pa__init(pa_module*m) {      pa_iochannel *io;      pa_modargs *ma; -    int fd, r = -1; +    int32_t fd; +    int r = -1;      pa_native_options *options = NULL;      pa_assert(m); @@ -63,18 +64,16 @@ int pa__init(pa_module*m) {          goto finish;      } -    options = pa_native_options_new(); -    options->module = m; -    options->auth_anonymous = TRUE; +    m->userdata = pa_native_protocol_get(m->core);      io = pa_iochannel_new(m->core->mainloop, fd, fd); -    m->userdata = pa_native_protocol_get(m->core); +    options = pa_native_options_new(); +    options->module = m; +    options->auth_anonymous = TRUE;      pa_native_protocol_connect(m->userdata, io, options); -    pa_native_options_unref(options); -      r = 0;  finish: diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c index 9162960f..470c622e 100644 --- a/src/modules/module-null-sink.c +++ b/src/modules/module-null-sink.c @@ -137,13 +137,13 @@ static void process_rewind(struct userdata *u, pa_usec_t now) {      pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);      if (u->timestamp <= now) -        return; +        goto do_nothing;      delay = u->timestamp - now;      in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec);      if (in_buffer <= 0) -        return; +        goto do_nothing;      if (rewind_nbytes > in_buffer)          rewind_nbytes = in_buffer; @@ -152,6 +152,11 @@ static void process_rewind(struct userdata *u, pa_usec_t now) {      u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec);      pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); +    return; + +do_nothing: + +    pa_sink_process_rewind(u->sink, 0);  }  static void process_render(struct userdata *u, pa_usec_t now) { diff --git a/src/modules/module-oss.c b/src/modules/module-oss.c index 15b1e956..23a32549 100644 --- a/src/modules/module-oss.c +++ b/src/modules/module-oss.c @@ -258,7 +258,7 @@ static int mmap_write(struct userdata *u) {      u->out_mmap_saved_nfrags = 0;      if (info.blocks > 0) -        mmap_fill_memblocks(u, info.blocks); +        mmap_fill_memblocks(u, (unsigned) info.blocks);      return info.blocks;  } @@ -336,7 +336,7 @@ static int mmap_read(struct userdata *u) {      u->in_mmap_saved_nfrags = 0;      if (info.blocks > 0) { -        mmap_post_memblocks(u, info.blocks); +        mmap_post_memblocks(u, (unsigned) info.blocks);          mmap_clear_memblocks(u, u->in_nfrags/2);      } @@ -356,12 +356,12 @@ static pa_usec_t mmap_sink_get_latency(struct userdata *u) {      u->out_mmap_saved_nfrags += info.blocks; -    bpos = ((u->out_mmap_current + u->out_mmap_saved_nfrags) * u->out_fragment_size) % u->out_hwbuf_size; +    bpos = ((u->out_mmap_current + (unsigned) u->out_mmap_saved_nfrags) * u->out_fragment_size) % u->out_hwbuf_size;      if (bpos <= (size_t) info.ptr) -        n = u->out_hwbuf_size - (info.ptr - bpos); +        n = u->out_hwbuf_size - ((size_t) info.ptr - bpos);      else -        n = bpos - info.ptr; +        n = bpos - (size_t) info.ptr;  /*     pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->out_fragment_size, u->out_fragments); */ @@ -380,12 +380,12 @@ static pa_usec_t mmap_source_get_latency(struct userdata *u) {      }      u->in_mmap_saved_nfrags += info.blocks; -    bpos = ((u->in_mmap_current + u->in_mmap_saved_nfrags) * u->in_fragment_size) % u->in_hwbuf_size; +    bpos = ((u->in_mmap_current + (unsigned) u->in_mmap_saved_nfrags) * u->in_fragment_size) % u->in_hwbuf_size;      if (bpos <= (size_t) info.ptr) -        n = info.ptr - bpos; +        n = (size_t) info.ptr - bpos;      else -        n = u->in_hwbuf_size - bpos + info.ptr; +        n = u->in_hwbuf_size - bpos + (size_t) info.ptr;  /*     pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->in_fragment_size, u->in_fragments);  */ @@ -404,7 +404,7 @@ static pa_usec_t io_sink_get_latency(struct userdata *u) {              pa_log_info("Device doesn't support SNDCTL_DSP_GETODELAY: %s", pa_cstrerror(errno));              u->use_getodelay = 0;          } else -            r = pa_bytes_to_usec(arg, &u->sink->sample_spec); +            r = pa_bytes_to_usec((size_t) arg, &u->sink->sample_spec);      } @@ -415,7 +415,7 @@ static pa_usec_t io_sink_get_latency(struct userdata *u) {              pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno));              u->use_getospace = 0;          } else -            r = pa_bytes_to_usec(info.bytes, &u->sink->sample_spec); +            r = pa_bytes_to_usec((size_t) info.bytes, &u->sink->sample_spec);      }      if (u->memchunk.memblock) @@ -437,7 +437,7 @@ static pa_usec_t io_source_get_latency(struct userdata *u) {              pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno));              u->use_getispace = 0;          } else -            r = pa_bytes_to_usec(info.bytes, &u->source->sample_spec); +            r = pa_bytes_to_usec((size_t) info.bytes, &u->source->sample_spec);      }      return r; @@ -528,8 +528,9 @@ static int unsuspend(struct userdata *u) {      if ((u->fd = pa_oss_open(u->device_name, &m, NULL)) < 0) {          pa_log_warn("Resume failed, device busy (%s)", pa_cstrerror(errno));          return -1; +    } -    if (m != u->mode) +    if (m != u->mode) {          pa_log_warn("Resume failed, couldn't open device with original access mode.");          goto fail;      } @@ -859,7 +860,7 @@ static int source_set_volume(pa_source *s) {  static void thread_func(void *userdata) {      struct userdata *u = userdata;      int write_type = 0, read_type = 0; -    unsigned short revents = 0; +    short revents = 0;      pa_assert(u); @@ -898,7 +899,7 @@ static void thread_func(void *userdata) {                  ssize_t l;                  pa_bool_t loop = FALSE, work_done = FALSE; -                l = u->out_fragment_size; +                l = (ssize_t) u->out_fragment_size;                  if (u->use_getospace) {                      audio_buf_info info; @@ -919,14 +920,14 @@ static void thread_func(void *userdata) {                  /* Round down to multiples of the fragment size,                   * because OSS needs that (at least some versions                   * do) */ -                l = (l/u->out_fragment_size) * u->out_fragment_size; +                l = (l/(ssize_t) u->out_fragment_size) * (ssize_t) u->out_fragment_size;                  /* Hmm, so poll() signalled us that we can read                   * something, but GETOSPACE told us there was nothing?                   * Hmm, make the best of it, try to read some data, to                   * avoid spinning forever. */                  if (l <= 0 && (revents & POLLOUT)) { -                    l = u->out_fragment_size; +                    l = (ssize_t) u->out_fragment_size;                      loop = FALSE;                  } @@ -935,7 +936,7 @@ static void thread_func(void *userdata) {                      ssize_t t;                      if (u->memchunk.length <= 0) -                        pa_sink_render(u->sink, l, &u->memchunk); +                        pa_sink_render(u->sink, (size_t) l, &u->memchunk);                      pa_assert(u->memchunk.length > 0); @@ -965,8 +966,8 @@ static void thread_func(void *userdata) {                      } else { -                        u->memchunk.index += t; -                        u->memchunk.length -= t; +                        u->memchunk.index += (size_t) t; +                        u->memchunk.length -= (size_t) t;                          if (u->memchunk.length <= 0) {                              pa_memblock_unref(u->memchunk.memblock); @@ -1009,7 +1010,7 @@ static void thread_func(void *userdata) {                  pa_memchunk memchunk;                  pa_bool_t loop = FALSE, work_done = FALSE; -                l = u->in_fragment_size; +                l = (ssize_t) u->in_fragment_size;                  if (u->use_getispace) {                      audio_buf_info info; @@ -1023,15 +1024,16 @@ static void thread_func(void *userdata) {                      }                  } -                l = (l/u->in_fragment_size) * u->in_fragment_size; +                l = (l/(ssize_t) u->in_fragment_size) * (ssize_t) u->in_fragment_size;                  if (l <= 0 && (revents & POLLIN)) { -                    l = u->in_fragment_size; +                    l = (ssize_t) u->in_fragment_size;                      loop = FALSE;                  }                  while (l > 0) { -                    ssize_t t, k; +                    ssize_t t; +                    size_t k;                      pa_assert(l > 0); @@ -1039,8 +1041,8 @@ static void thread_func(void *userdata) {                      k = pa_memblock_get_length(memchunk.memblock); -                    if (k > l) -                        k = l; +                    if (k > (size_t) l) +                        k = (size_t) l;                      k = (k/u->frame_size)*u->frame_size; @@ -1071,7 +1073,7 @@ static void thread_func(void *userdata) {                      } else {                          memchunk.index = 0; -                        memchunk.length = t; +                        memchunk.length = (size_t) t;                          pa_source_post(u->source, &memchunk);                          pa_memblock_unref(memchunk.memblock); @@ -1099,9 +1101,9 @@ static void thread_func(void *userdata) {              pa_assert(u->fd >= 0);              pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); -            pollfd->events = -                ((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) | -                ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0); +            pollfd->events = (short) +                (((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) | +                 ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0));          }          /* Hmm, nothing to do. Let's sleep */ @@ -1179,10 +1181,10 @@ int pa__init(pa_module*m) {          goto fail;      } -    nfrags = m->core->default_n_fragments; -    frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss); +    nfrags = (int) m->core->default_n_fragments; +    frag_size = (int) pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss);      if (frag_size <= 0) -        frag_size = pa_frame_size(&ss); +        frag_size = (int) pa_frame_size(&ss);      if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {          pa_log("Failed to parse fragments arguments"); @@ -1238,8 +1240,8 @@ int pa__init(pa_module*m) {      u->mode = mode;      u->frame_size = pa_frame_size(&ss);      u->device_name = pa_xstrdup(dev); -    u->in_nfrags = u->out_nfrags = u->nfrags = nfrags; -    u->out_fragment_size = u->in_fragment_size = u->frag_size = frag_size; +    u->in_nfrags = u->out_nfrags = (uint32_t) (u->nfrags = nfrags); +    u->out_fragment_size = u->in_fragment_size = (uint32_t) (u->frag_size = frag_size);      u->use_mmap = use_mmap;      u->rtpoll = pa_rtpoll_new();      pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); @@ -1248,15 +1250,15 @@ int pa__init(pa_module*m) {      if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {          pa_log_info("Input -- %u fragments of size %u.", info.fragstotal, info.fragsize); -        u->in_fragment_size = info.fragsize; -        u->in_nfrags = info.fragstotal; +        u->in_fragment_size = (uint32_t) info.fragsize; +        u->in_nfrags = (uint32_t) info.fragstotal;          u->use_getispace = TRUE;      }      if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {          pa_log_info("Output -- %u fragments of size %u.", info.fragstotal, info.fragsize); -        u->out_fragment_size = info.fragsize; -        u->out_nfrags = info.fragstotal; +        u->out_fragment_size = (uint32_t) info.fragsize; +        u->out_nfrags = (uint32_t) info.fragstotal;          u->use_getospace = TRUE;      } diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c index f389cd06..ae230b2c 100644 --- a/src/modules/module-pipe-sink.c +++ b/src/modules/module-pipe-sink.c @@ -145,8 +145,8 @@ static int process_render(struct userdata *u) {          } else { -            u->memchunk.index += l; -            u->memchunk.length -= l; +            u->memchunk.index += (size_t) l; +            u->memchunk.length -= (size_t) l;              if (u->memchunk.length <= 0) {                  pa_memblock_unref(u->memchunk.memblock); @@ -189,7 +189,7 @@ static void thread_func(void *userdata) {          }          /* Hmm, nothing to do. Let's sleep */ -        pollfd->events = u->sink->thread_info.state == PA_SINK_RUNNING ? POLLOUT : 0; +        pollfd->events = (short) (u->sink->thread_info.state == PA_SINK_RUNNING ? POLLOUT : 0);          if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)              goto fail; diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c index b0de34ca..25151d95 100644 --- a/src/modules/module-pipe-source.c +++ b/src/modules/module-pipe-source.c @@ -135,9 +135,9 @@ static void thread_func(void *userdata) {              } else { -                u->memchunk.length = l; +                u->memchunk.length = (size_t) l;                  pa_source_post(u->source, &u->memchunk); -                u->memchunk.index += l; +                u->memchunk.index += (size_t) l;                  if (u->memchunk.index >= pa_memblock_get_length(u->memchunk.memblock)) {                      pa_memblock_unref(u->memchunk.memblock); @@ -149,7 +149,7 @@ static void thread_func(void *userdata) {          }          /* Hmm, nothing to do. Let's sleep */ -        pollfd->events = u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0; +        pollfd->events = (short) (u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0);          if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)              goto fail; diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c index 8136c6fc..aefd4020 100644 --- a/src/modules/module-protocol-stub.c +++ b/src/modules/module-protocol-stub.c @@ -148,7 +148,7 @@  #  include <pulsecore/esound.h>  #  define TCPWRAP_SERVICE "esound"  #  define IPV4_PORT ESD_DEFAULT_PORT -#  define MODULE_ARGUMENTS_COMMON "sink", "source", "auth-anonymous", "cookie", "auth-cookie", "auth-cookie-enabled" +#  define MODULE_ARGUMENTS_COMMON "sink", "source", "auth-anonymous", "cookie", "auth-cookie", "auth-cookie-enabled",  #  ifdef USE_TCP_SOCKETS  #    include "module-esound-protocol-tcp-symdef.h" @@ -260,7 +260,7 @@ int pa__init(pa_module*m) {          goto fail;      } -    u = pa_xnew0(struct userdata, 1); +    m->userdata = u = pa_xnew0(struct userdata, 1);      u->module = m;  #if defined(USE_PROTOCOL_SIMPLE) @@ -299,11 +299,11 @@ int pa__init(pa_module*m) {      listen_on = pa_modargs_get_value(ma, "listen", NULL);      if (listen_on) { -        u->socket_server_ipv6 = pa_socket_server_new_ipv6_string(m->core->mainloop, listen_on, port, TCPWRAP_SERVICE); -        u->socket_server_ipv4 = pa_socket_server_new_ipv4_string(m->core->mainloop, listen_on, port, TCPWRAP_SERVICE); +        u->socket_server_ipv6 = pa_socket_server_new_ipv6_string(m->core->mainloop, listen_on, (uint16_t) port, TCPWRAP_SERVICE); +        u->socket_server_ipv4 = pa_socket_server_new_ipv4_string(m->core->mainloop, listen_on, (uint16_t) port, TCPWRAP_SERVICE);      } else { -        u->socket_server_ipv6 = pa_socket_server_new_ipv6_any(m->core->mainloop, port, TCPWRAP_SERVICE); -        u->socket_server_ipv4 = pa_socket_server_new_ipv4_any(m->core->mainloop, port, TCPWRAP_SERVICE); +        u->socket_server_ipv6 = pa_socket_server_new_ipv6_any(m->core->mainloop, (uint16_t) port, TCPWRAP_SERVICE); +        u->socket_server_ipv4 = pa_socket_server_new_ipv4_any(m->core->mainloop, (uint16_t) port, TCPWRAP_SERVICE);      }      if (!u->socket_server_ipv4 && !u->socket_server_ipv6) @@ -327,7 +327,7 @@ int pa__init(pa_module*m) {      /* This socket doesn't reside in our own runtime dir but in       * /tmp/.esd/, hence we have to create the dir first */ -    if (pa_make_secure_parent_dir(u->socket_path, pa_in_system_mode() ? 0755 : 0700, (uid_t)-1, (gid_t)-1) < 0) { +    if (pa_make_secure_parent_dir(u->socket_path, pa_in_system_mode() ? 0755U : 0700U, (uid_t)-1, (gid_t)-1) < 0) {          pa_log("Failed to create socket directory '%s': %s\n", u->socket_path, pa_cstrerror(errno));          goto fail;      } @@ -368,8 +368,6 @@ int pa__init(pa_module*m) {  #  endif  #endif -    m->userdata = u; -      if (ma)          pa_modargs_free(ma); @@ -390,7 +388,8 @@ void pa__done(pa_module*m) {      pa_assert(m); -    u = m->userdata; +    if (!(u = m->userdata)) +        return;  #if defined(USE_PROTOCOL_SIMPLE)      if (u->simple_protocol) { diff --git a/src/modules/module-raop-discover.c b/src/modules/module-raop-discover.c new file mode 100644 index 00000000..3706d921 --- /dev/null +++ b/src/modules/module-raop-discover.c @@ -0,0 +1,380 @@ +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 Lennart Poettering +  Copyright 2008 Colin Guthrie + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as +  published by the Free Software Foundation; either version 2 of the +  License, or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public +  License along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <avahi-client/client.h> +#include <avahi-client/lookup.h> +#include <avahi-common/alternative.h> +#include <avahi-common/error.h> +#include <avahi-common/domain.h> +#include <avahi-common/malloc.h> + +#include <pulse/xmalloc.h> +#include <pulse/util.h> + +#include <pulsecore/sink.h> +#include <pulsecore/source.h> +#include <pulsecore/native-common.h> +#include <pulsecore/core-util.h> +#include <pulsecore/log.h> +#include <pulsecore/core-subscribe.h> +#include <pulsecore/hashmap.h> +#include <pulsecore/modargs.h> +#include <pulsecore/namereg.h> +#include <pulsecore/avahi-wrap.h> + +#include "module-raop-discover-symdef.h" + +PA_MODULE_AUTHOR("Colin Guthrie"); +PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Discovery of Airtunes"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); + +#define SERVICE_TYPE_SINK "_raop._tcp" + +static const char* const valid_modargs[] = { +    NULL +}; + +struct tunnel { +    AvahiIfIndex interface; +    AvahiProtocol protocol; +    char *name, *type, *domain; +    uint32_t module_index; +}; + +struct userdata { +    pa_core *core; +    pa_module *module; +    AvahiPoll *avahi_poll; +    AvahiClient *client; +    AvahiServiceBrowser *sink_browser; + +    pa_hashmap *tunnels; +}; + +static unsigned tunnel_hash(const void *p) { +    const struct tunnel *t = p; + +    return +        (unsigned) t->interface + +        (unsigned) t->protocol + +        pa_idxset_string_hash_func(t->name) + +        pa_idxset_string_hash_func(t->type) + +        pa_idxset_string_hash_func(t->domain); +} + +static int tunnel_compare(const void *a, const void *b) { +    const struct tunnel *ta = a, *tb = b; +    int r; + +    if (ta->interface != tb->interface) +        return 1; +    if (ta->protocol != tb->protocol) +        return 1; +    if ((r = strcmp(ta->name, tb->name))) +        return r; +    if ((r = strcmp(ta->type, tb->type))) +        return r; +    if ((r = strcmp(ta->domain, tb->domain))) +        return r; + +    return 0; +} + +static struct tunnel *tunnel_new( +        AvahiIfIndex interface, AvahiProtocol protocol, +        const char *name, const char *type, const char *domain) { + +    struct tunnel *t; +    t = pa_xnew(struct tunnel, 1); +    t->interface = interface; +    t->protocol = protocol; +    t->name = pa_xstrdup(name); +    t->type = pa_xstrdup(type); +    t->domain = pa_xstrdup(domain); +    t->module_index = PA_IDXSET_INVALID; +    return t; +} + +static void tunnel_free(struct tunnel *t) { +    pa_assert(t); +    pa_xfree(t->name); +    pa_xfree(t->type); +    pa_xfree(t->domain); +    pa_xfree(t); +} + +static void resolver_cb( +        AvahiServiceResolver *r, +        AvahiIfIndex interface, AvahiProtocol protocol, +        AvahiResolverEvent event, +        const char *name, const char *type, const char *domain, +        const char *host_name, const AvahiAddress *a, uint16_t port, +        AvahiStringList *txt, +        AvahiLookupResultFlags flags, +        void *userdata) { + +    struct userdata *u = userdata; +    struct tunnel *tnl; + +    pa_assert(u); + +    tnl = tunnel_new(interface, protocol, name, type, domain); + +    if (event != AVAHI_RESOLVER_FOUND) +        pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client))); +    else { +        char *device = NULL, *dname, *vname, *args; +        char at[AVAHI_ADDRESS_STR_MAX]; +        AvahiStringList *l; +        pa_module *m; + +        for (l = txt; l; l = l->next) { +            char *key, *value; +            pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0); + +            pa_log_debug("Found key: '%s' with value: '%s'", key, value); +            if (strcmp(key, "device") == 0) { +                pa_xfree(device); +                device = value; +                value = NULL; +            } +            avahi_free(key); +            avahi_free(value); +        } + +        if (device) +            dname = pa_sprintf_malloc("airtunes.%s.%s", host_name, device); +        else +            dname = pa_sprintf_malloc("airtunes.%s", host_name); + +        if (!(vname = pa_namereg_make_valid_name(dname))) { +            pa_log("Cannot construct valid device name from '%s'.", dname); +            avahi_free(device); +            pa_xfree(dname); +            goto finish; +        } +        pa_xfree(dname); + +        /* +         TODO: allow this syntax of server name in things.... +        args = pa_sprintf_malloc("server=[%s]:%u " +                                 "sink_name=%s", +                                 avahi_address_snprint(at, sizeof(at), a), port, +                                 vname);*/ +        args = pa_sprintf_malloc("server=%s " +                                 "sink_name=%s", +                                 avahi_address_snprint(at, sizeof(at), a), +                                 vname); + +        pa_log_debug("Loading module-raop-sink with arguments '%s'", args); + +        if ((m = pa_module_load(u->core, "module-raop-sink", args))) { +            tnl->module_index = m->index; +            pa_hashmap_put(u->tunnels, tnl, tnl); +            tnl = NULL; +        } + +        pa_xfree(vname); +        pa_xfree(args); +        avahi_free(device); +    } + +finish: + +    avahi_service_resolver_free(r); + +    if (tnl) +        tunnel_free(tnl); +} + +static void browser_cb( +        AvahiServiceBrowser *b, +        AvahiIfIndex interface, AvahiProtocol protocol, +        AvahiBrowserEvent event, +        const char *name, const char *type, const char *domain, +        AvahiLookupResultFlags flags, +        void *userdata) { + +    struct userdata *u = userdata; +    struct tunnel *t; + +    pa_assert(u); + +    if (flags & AVAHI_LOOKUP_RESULT_LOCAL) +        return; + +    t = tunnel_new(interface, protocol, name, type, domain); + +    if (event == AVAHI_BROWSER_NEW) { + +        if (!pa_hashmap_get(u->tunnels, t)) +            if (!(avahi_service_resolver_new(u->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolver_cb, u))) +                pa_log("avahi_service_resolver_new() failed: %s", avahi_strerror(avahi_client_errno(u->client))); + +        /* We ignore the returned resolver object here, since the we don't +         * need to attach any special data to it, and we can still destory +         * it from the callback */ + +    } else if (event == AVAHI_BROWSER_REMOVE) { +        struct tunnel *t2; + +        if ((t2 = pa_hashmap_get(u->tunnels, t))) { +            pa_module_unload_by_index(u->core, t2->module_index, TRUE); +            pa_hashmap_remove(u->tunnels, t2); +            tunnel_free(t2); +        } +    } + +    tunnel_free(t); +} + +static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) { +    struct userdata *u = userdata; + +    pa_assert(c); +    pa_assert(u); + +    u->client = c; + +    switch (state) { +        case AVAHI_CLIENT_S_REGISTERING: +        case AVAHI_CLIENT_S_RUNNING: +        case AVAHI_CLIENT_S_COLLISION: + +            if (!u->sink_browser) { + +                if (!(u->sink_browser = avahi_service_browser_new( +                              c, +                              AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, +                              SERVICE_TYPE_SINK, +                              NULL, +                              0, +                              browser_cb, u))) { + +                    pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c))); +                    pa_module_unload_request(u->module, TRUE); +                } +            } + +            break; + +        case AVAHI_CLIENT_FAILURE: +            if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) { +                int error; + +                pa_log_debug("Avahi daemon disconnected."); + +                if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) { +                    pa_log("avahi_client_new() failed: %s", avahi_strerror(error)); +                    pa_module_unload_request(u->module, TRUE); +                } +            } + +            /* Fall through */ + +        case AVAHI_CLIENT_CONNECTING: + +            if (u->sink_browser) { +                avahi_service_browser_free(u->sink_browser); +                u->sink_browser = NULL; +            } + +            break; + +        default: ; +    } +} + +int pa__init(pa_module*m) { + +    struct userdata *u; +    pa_modargs *ma = NULL; +    int error; + +    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { +        pa_log("Failed to parse module arguments."); +        goto fail; +    } + +    m->userdata = u = pa_xnew(struct userdata, 1); +    u->core = m->core; +    u->module = m; +    u->sink_browser = NULL; + +    u->tunnels = pa_hashmap_new(tunnel_hash, tunnel_compare); + +    u->avahi_poll = pa_avahi_poll_new(m->core->mainloop); + +    if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) { +        pa_log("pa_avahi_client_new() failed: %s", avahi_strerror(error)); +        goto fail; +    } + +    pa_modargs_free(ma); + +    return 0; + +fail: +    pa__done(m); + +    if (ma) +        pa_modargs_free(ma); + +    return -1; +} + +void pa__done(pa_module*m) { +    struct userdata*u; +    pa_assert(m); + +    if (!(u = m->userdata)) +        return; + +    if (u->client) +        avahi_client_free(u->client); + +    if (u->avahi_poll) +        pa_avahi_poll_free(u->avahi_poll); + +    if (u->tunnels) { +        struct tunnel *t; + +        while ((t = pa_hashmap_steal_first(u->tunnels))) { +            pa_module_unload_by_index(u->core, t->module_index, TRUE); +            tunnel_free(t); +        } + +        pa_hashmap_free(u->tunnels, NULL, NULL); +    } + +    pa_xfree(u); +} diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c new file mode 100644 index 00000000..62f0a73c --- /dev/null +++ b/src/modules/module-raop-sink.c @@ -0,0 +1,675 @@ +/*** +  This file is part of PulseAudio. + +  Copyright 2004-2006 Lennart Poettering +  Copyright 2008 Colin Guthrie + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <sys/stat.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> +#include <poll.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/ioctl.h> + +#ifdef HAVE_LINUX_SOCKIOS_H +#include <linux/sockios.h> +#endif + +#include <pulse/xmalloc.h> +#include <pulse/timeval.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/iochannel.h> +#include <pulsecore/sink.h> +#include <pulsecore/module.h> +#include <pulsecore/core-util.h> +#include <pulsecore/modargs.h> +#include <pulsecore/log.h> +#include <pulsecore/socket-client.h> +#include <pulsecore/authkey.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/thread.h> +#include <pulsecore/time-smoother.h> +#include <pulsecore/rtclock.h> +#include <pulsecore/socket-util.h> + +#include "module-raop-sink-symdef.h" +#include "rtp.h" +#include "sdp.h" +#include "sap.h" +#include "raop_client.h" + +PA_MODULE_AUTHOR("Colin Guthrie"); +PA_MODULE_DESCRIPTION("RAOP Sink (Apple Airtunes)"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(FALSE); +PA_MODULE_USAGE( +        "sink_name=<name for the sink> " +        "server=<address>  " +        "format=<sample format> " +        "channels=<number of channels> " +        "rate=<sample rate>"); + +#define DEFAULT_SINK_NAME "airtunes" + +struct userdata { +    pa_core *core; +    pa_module *module; +    pa_sink *sink; + +    pa_thread_mq thread_mq; +    pa_rtpoll *rtpoll; +    pa_rtpoll_item *rtpoll_item; +    pa_thread *thread; + +    pa_memchunk raw_memchunk; +    pa_memchunk encoded_memchunk; + +    void *write_data; +    size_t write_length, write_index; + +    void *read_data; +    size_t read_length, read_index; + +    pa_usec_t latency; + +    pa_volume_t volume; +    pa_bool_t muted; + +    /*esd_format_t format;*/ +    int32_t rate; + +    pa_smoother *smoother; +    int fd; + +    int64_t offset; +    int64_t encoding_overhead; +    int32_t next_encoding_overhead; +    double encoding_ratio; + +    pa_raop_client *raop; + +    size_t block_size; +}; + +static const char* const valid_modargs[] = { +    "server", +    "rate", +    "format", +    "channels", +    "sink_name", +    NULL +}; + +enum { +    SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX, +    SINK_MESSAGE_RIP_SOCKET +}; + +static void on_connection(PA_GCC_UNUSED int fd, void*userdata) { +    struct userdata *u = userdata; +    pa_assert(u); + +    pa_assert(u->fd < 0); +    u->fd = fd; + +    /* Set the initial volume */ +    pa_raop_client_set_volume(u->raop, u->volume); + +    pa_log_debug("Connection authenticated, handing fd to IO thread..."); + +    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL); +} + +static void on_close(void*userdata) { +    struct userdata *u = userdata; +    pa_assert(u); + +    pa_log_debug("Connection closed, informing IO thread..."); + +    pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RIP_SOCKET, NULL, 0, NULL, NULL); +} + +static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { +    struct userdata *u = PA_SINK(o)->userdata; + +    switch (code) { + +        case PA_SINK_MESSAGE_SET_STATE: + +            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { + +                case PA_SINK_SUSPENDED: +                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state)); + +                    pa_smoother_pause(u->smoother, pa_rtclock_usec()); + +                    /* Issue a FLUSH if we are connected */ +                    if (u->fd >= 0) { +                        pa_raop_flush(u->raop); +                    } +                    break; + +                case PA_SINK_IDLE: +                case PA_SINK_RUNNING: + +                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { +                        pa_smoother_resume(u->smoother, pa_rtclock_usec()); + +                        /* The connection can be closed when idle, so check to +                           see if we need to reestablish it */ +                        if (u->fd < 0) +                            pa_raop_connect(u->raop); +                        else +                            pa_raop_flush(u->raop); +                    } + +                    break; + +                case PA_SINK_UNLINKED: +                case PA_SINK_INIT: +                    ; +            } + +            break; + +        case PA_SINK_MESSAGE_GET_LATENCY: { +            pa_usec_t w, r; + +            r = pa_smoother_get(u->smoother, pa_rtclock_usec()); +            w = pa_bytes_to_usec((u->offset - u->encoding_overhead + (u->encoded_memchunk.length / u->encoding_ratio)), &u->sink->sample_spec); + +            *((pa_usec_t*) data) = w > r ? w - r : 0; +            break; +        } + +        case SINK_MESSAGE_PASS_SOCKET: { +            struct pollfd *pollfd; + +            pa_assert(!u->rtpoll_item); + +            u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); +            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); +            pollfd->fd = u->fd; +            pollfd->events = POLLOUT; +            /*pollfd->events = */pollfd->revents = 0; + +            if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { +                /* Our stream has been suspended so we just flush it.... */ +                pa_raop_flush(u->raop); +            } +            return 0; +        } + +        case SINK_MESSAGE_RIP_SOCKET: { +            pa_assert(u->fd >= 0); + +            pa_close(u->fd); +            u->fd = -1; + +            if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { + +                pa_log_debug("RTSP control connection closed, but we're suspended so let's not worry about it... we'll open it again later"); + +                if (u->rtpoll_item) +                    pa_rtpoll_item_free(u->rtpoll_item); +                u->rtpoll_item = NULL; +            } else { +                /* Quesiton: is this valid here: or should we do some sort of: +                   return pa_sink_process_msg(PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL); +                   ?? */ +                pa_module_unload_request(u->module, TRUE); +            } +            return 0; +        } +    } + +    return pa_sink_process_msg(o, code, data, offset, chunk); +} + +static int sink_get_volume_cb(pa_sink *s) { +    struct userdata *u = s->userdata; +    int i; + +    pa_assert(u); + +    for (i = 0; i < s->sample_spec.channels; i++) { +        s->volume.values[i] = u->volume; +    } + +    return 0; +} + +static int sink_set_volume_cb(pa_sink *s) { +    struct userdata *u = s->userdata; +    int rv; + +    pa_assert(u); + +    /* If we're muted, we fake it */ +    if (u->muted) +        return 0; + +    pa_assert(s->sample_spec.channels > 0); + +    /* Avoid pointless volume sets */ +    if (u->volume == s->volume.values[0]) +        return 0; + +    rv = pa_raop_client_set_volume(u->raop, s->volume.values[0]); +    if (0 == rv) +        u->volume = s->volume.values[0]; + +    return rv; +} + +static int sink_get_mute_cb(pa_sink *s) { +    struct userdata *u = s->userdata; + +    pa_assert(u); + +    s->muted = u->muted; +    return 0; +} + +static int sink_set_mute_cb(pa_sink *s) { +    struct userdata *u = s->userdata; +    int rv; + +    pa_assert(u); + +    rv = pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume)); +    u->muted = s->muted; +    return rv; +} + +static void thread_func(void *userdata) { +    struct userdata *u = userdata; +    int write_type = 0; +    pa_memchunk silence; +    uint32_t silence_overhead = 0; +    double silence_ratio = 0; + +    pa_assert(u); + +    pa_log_debug("Thread starting up"); + +    pa_thread_mq_install(&u->thread_mq); +    pa_rtpoll_install(u->rtpoll); + +    pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); + +    /* Create a chunk of memory that is our encoded silence sample. */ +    pa_memchunk_reset(&silence); + +    for (;;) { +        int ret; + +        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) +            if (u->sink->thread_info.rewind_requested) +                pa_sink_process_rewind(u->sink, 0); + +        if (u->rtpoll_item) { +            struct pollfd *pollfd; +            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + +            /* Render some data and write it to the fifo */ +            if (/*PA_SINK_IS_OPENED(u->sink->thread_info.state) && */pollfd->revents) { +                pa_usec_t usec; +                int64_t n; +                void *p; + +                if (!silence.memblock) { +                    pa_memchunk silence_tmp; + +                    pa_memchunk_reset(&silence_tmp); +                    silence_tmp.memblock = pa_memblock_new(u->core->mempool, 4096); +                    silence_tmp.length = 4096; +                    p = pa_memblock_acquire(silence_tmp.memblock); +                      memset(p, 0, 4096); +                    pa_memblock_release(silence_tmp.memblock); +                    pa_raop_client_encode_sample(u->raop, &silence_tmp, &silence); +                    pa_assert(0 == silence_tmp.length); +                    silence_overhead = silence_tmp.length - 4096; +                    silence_ratio = silence_tmp.length / 4096; +                    pa_memblock_unref(silence_tmp.memblock); +                } + +                for (;;) { +                    ssize_t l; + +                    if (u->encoded_memchunk.length <= 0) { +                        if (u->encoded_memchunk.memblock) +                            pa_memblock_unref(u->encoded_memchunk.memblock); +                        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { +                            size_t rl; + +                            /* We render real data */ +                            if (u->raw_memchunk.length <= 0) { +                                if (u->raw_memchunk.memblock) +                                    pa_memblock_unref(u->raw_memchunk.memblock); +                                pa_memchunk_reset(&u->raw_memchunk); + +                                /* Grab unencoded data */ +                                pa_sink_render(u->sink, u->block_size, &u->raw_memchunk); +                            } +                            pa_assert(u->raw_memchunk.length > 0); + +                            /* Encode it */ +                            rl = u->raw_memchunk.length; +                            u->encoding_overhead += u->next_encoding_overhead; +                            pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk); +                            u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length)); +                            u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length); +                        } else { +                            /* We render some silence into our memchunk */ +                            memcpy(&u->encoded_memchunk, &silence, sizeof(pa_memchunk)); +                            pa_memblock_ref(silence.memblock); + +                            /* Calculate/store some values to be used with the smoother */ +                            u->next_encoding_overhead = silence_overhead; +                            u->encoding_ratio = silence_ratio; +                        } +                    } +                    pa_assert(u->encoded_memchunk.length > 0); + +                    p = pa_memblock_acquire(u->encoded_memchunk.memblock); +                    l = pa_write(u->fd, (uint8_t*) p + u->encoded_memchunk.index, u->encoded_memchunk.length, &write_type); +                    pa_memblock_release(u->encoded_memchunk.memblock); + +                    pa_assert(l != 0); + +                    if (l < 0) { + +                        if (errno == EINTR) +                            continue; +                        else if (errno == EAGAIN) { + +                            /* OK, we filled all socket buffers up +                             * now. */ +                            goto filled_up; + +                        } else { +                            pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); +                            goto fail; +                        } + +                    } else { +                        u->offset += l; + +                        u->encoded_memchunk.index += l; +                        u->encoded_memchunk.length -= l; + +                        pollfd->revents = 0; + +                        if (u->encoded_memchunk.length > 0) { +                            /* we've completely written the encoded data, so update our overhead */ +                            u->encoding_overhead += u->next_encoding_overhead; + +                            /* OK, we wrote less that we asked for, +                             * hence we can assume that the socket +                             * buffers are full now */ +                            goto filled_up; +                        } +                    } +                } + +            filled_up: + +                /* At this spot we know that the socket buffers are +                 * fully filled up. This is the best time to estimate +                 * the playback position of the server */ + +                n = u->offset - u->encoding_overhead; + +#ifdef SIOCOUTQ +                { +                    int l; +                    if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0) +                        n -= (l / u->encoding_ratio); +                } +#endif + +                usec = pa_bytes_to_usec(n, &u->sink->sample_spec); + +                if (usec > u->latency) +                    usec -= u->latency; +                else +                    usec = 0; + +                pa_smoother_put(u->smoother, pa_rtclock_usec(), usec); +            } + +            /* Hmm, nothing to do. Let's sleep */ +            pollfd->events = POLLOUT; /*PA_SINK_IS_OPENED(u->sink->thread_info.state)  ? POLLOUT : 0;*/ +        } + +        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) +            goto fail; + +        if (ret == 0) +            goto finish; + +        if (u->rtpoll_item) { +            struct pollfd* pollfd; + +            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + +            if (pollfd->revents & ~POLLOUT) { +                if (u->sink->thread_info.state != PA_SINK_SUSPENDED) { +                    pa_log("FIFO shutdown."); +                    goto fail; +                } + +                /* We expect this to happen on occasion if we are not sending data. +                   It's perfectly natural and normal and natural */ +                if (u->rtpoll_item) +                    pa_rtpoll_item_free(u->rtpoll_item); +                u->rtpoll_item = NULL; +            } +        } +    } + +fail: +    /* If this was no regular exit from the loop we have to continue +     * processing messages until we received PA_MESSAGE_SHUTDOWN */ +    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); +    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); + +finish: +    if (silence.memblock) +        pa_memblock_unref(silence.memblock); +    pa_log_debug("Thread shutting down"); +} + +int pa__init(pa_module*m) { +    struct userdata *u = NULL; +    pa_sample_spec ss; +    pa_modargs *ma = NULL; +    const char *server; +    pa_sink_new_data data; + +    pa_assert(m); + +    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { +        pa_log("failed to parse module arguments"); +        goto fail; +    } + +    ss = m->core->default_sample_spec; +    if (pa_modargs_get_sample_spec(ma, &ss) < 0) { +        pa_log("invalid sample format specification"); +        goto fail; +    } + +    if ((/*ss.format != PA_SAMPLE_U8 &&*/ ss.format != PA_SAMPLE_S16NE) || +        (ss.channels > 2)) { +        pa_log("sample type support is limited to mono/stereo and U8 or S16NE sample data"); +        goto fail; +    } + +    u = pa_xnew0(struct userdata, 1); +    u->core = m->core; +    u->module = m; +    m->userdata = u; +    u->fd = -1; +    u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10); +    pa_memchunk_reset(&u->raw_memchunk); +    pa_memchunk_reset(&u->encoded_memchunk); +    u->offset = 0; +    u->encoding_overhead = 0; +    u->next_encoding_overhead = 0; +    u->encoding_ratio = 1.0; + +    u->volume = roundf(0.7 * PA_VOLUME_NORM); +    u->muted = FALSE; + +    u->rtpoll = pa_rtpoll_new(); +    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); +    u->rtpoll_item = NULL; + +    /*u->format = +        (ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) | +        (ss.channels == 2 ? ESD_STEREO : ESD_MONO);*/ +    u->rate = ss.rate; +    u->block_size = pa_usec_to_bytes(PA_USEC_PER_SEC/20, &ss); + +    u->read_data = u->write_data = NULL; +    u->read_index = u->write_index = u->read_length = u->write_length = 0; + +    /*u->state = STATE_AUTH;*/ +    u->latency = 0; + +    if (!(server = pa_modargs_get_value(ma, "server", NULL))) { +        pa_log("No server argument given."); +        goto fail; +    } + +    pa_sink_new_data_init(&data); +    data.driver = __FILE__; +    data.module = m; +    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); +    pa_sink_new_data_set_sample_spec(&data, &ss); +    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server); +    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Airtunes sink '%s'", server); + +    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_NETWORK); +    pa_sink_new_data_done(&data); + +    if (!u->sink) { +        pa_log("Failed to create sink."); +        goto fail; +    } + +    u->sink->parent.process_msg = sink_process_msg; +    u->sink->userdata = u; +    u->sink->get_volume = sink_get_volume_cb; +    u->sink->set_volume = sink_set_volume_cb; +    u->sink->get_mute = sink_get_mute_cb; +    u->sink->set_mute = sink_set_mute_cb; +    u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK|PA_SINK_HW_VOLUME_CTRL; + +    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); +    pa_sink_set_rtpoll(u->sink, u->rtpoll); + +    if (!(u->raop = pa_raop_client_new(u->core, server))) { +        pa_log("Failed to connect to server."); +        goto fail; +    } + +    pa_raop_client_set_callback(u->raop, on_connection, u); +    pa_raop_client_set_closed_callback(u->raop, on_close, u); + +    if (!(u->thread = pa_thread_new(thread_func, u))) { +        pa_log("Failed to create thread."); +        goto fail; +    } + +    pa_sink_put(u->sink); + +    pa_modargs_free(ma); + +    return 0; + +fail: +    if (ma) +        pa_modargs_free(ma); + +    pa__done(m); + +    return -1; +} + +void pa__done(pa_module*m) { +    struct userdata *u; +    pa_assert(m); + +    if (!(u = m->userdata)) +        return; + +    if (u->sink) +        pa_sink_unlink(u->sink); + +    if (u->thread) { +        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); +        pa_thread_free(u->thread); +    } + +    pa_thread_mq_done(&u->thread_mq); + +    if (u->sink) +        pa_sink_unref(u->sink); + +    if (u->rtpoll_item) +        pa_rtpoll_item_free(u->rtpoll_item); + +    if (u->rtpoll) +        pa_rtpoll_free(u->rtpoll); + +    if (u->raw_memchunk.memblock) +        pa_memblock_unref(u->raw_memchunk.memblock); + +    if (u->encoded_memchunk.memblock) +        pa_memblock_unref(u->encoded_memchunk.memblock); + +    if (u->raop) +        pa_raop_client_free(u->raop); + +    pa_xfree(u->read_data); +    pa_xfree(u->write_data); + +    if (u->smoother) +        pa_smoother_free(u->smoother); + +    if (u->fd >= 0) +        pa_close(u->fd); + +    pa_xfree(u); +} diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 5b2be118..976a8ce5 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -274,6 +274,16 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s      }  } +/* Called from main context */ +static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { +    struct userdata *u; + +    pa_sink_input_assert_ref(i); +    pa_assert_se(u = i->userdata); + +    return u->sink != dest; +} +  int pa__init(pa_module*m) {      struct userdata *u;      pa_sample_spec ss; @@ -386,6 +396,7 @@ int pa__init(pa_module*m) {      u->sink_input->detach = sink_input_detach_cb;      u->sink_input->kill = sink_input_kill_cb;      u->sink_input->state_change = sink_input_state_change_cb; +    u->sink_input->may_move_to = sink_input_may_move_to_cb;      u->sink_input->userdata = u;      pa_sink_put(u->sink); diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c index a6324526..21565cc4 100644 --- a/src/modules/module-sine.c +++ b/src/modules/module-sine.c @@ -116,13 +116,13 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s          pa_sink_input_request_rewind(i, 0, FALSE, TRUE);  } -static void calc_sine(float *f, size_t l, float freq) { +static void calc_sine(float *f, size_t l, double freq) {      size_t i;      l /= sizeof(float);      for (i = 0; i < l; i++) -        f[i] = (float) sin((double) i/l*M_PI*2*freq)/2; +        f[i] = (float) sin((double) i/(double)l*M_PI*2*freq)/2;  }  int pa__init(pa_module*m) { @@ -163,7 +163,7 @@ int pa__init(pa_module*m) {      u->memblock = pa_memblock_new(m->core->mempool, pa_bytes_per_second(&ss));      p = pa_memblock_acquire(u->memblock); -    calc_sine(p, pa_memblock_get_length(u->memblock), frequency); +    calc_sine(p, pa_memblock_get_length(u->memblock), (double) frequency);      pa_memblock_release(u->memblock);      pa_sink_input_new_data_init(&data); diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index ec4e7c79..55897004 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -134,7 +134,7 @@ static char *get_name(pa_proplist *p, const char *prefix) {      else if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_NAME)))          return pa_sprintf_malloc("%s-by-media-name:%s", prefix, r); -    return NULL; +    return pa_sprintf_malloc("%s-fallback:%s", prefix, r);  }  static struct entry* read_entry(struct userdata *u, char *name) { @@ -145,7 +145,7 @@ static struct entry* read_entry(struct userdata *u, char *name) {      pa_assert(name);      key.dptr = name; -    key.dsize = strlen(name); +    key.dsize = (int) strlen(name);      data = gdbm_fetch(u->gdbm_file, key); @@ -277,7 +277,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3      }      key.dptr = name; -    key.dsize = strlen(name); +    key.dsize = (int) strlen(name);      data.dptr = (void*) &entry;      data.dsize = sizeof(entry); @@ -306,8 +306,11 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n          if (u->restore_device &&              (s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK, TRUE))) { -            pa_log_info("Restoring device for stream %s.", name); -            new_data->sink = s; +            if (!new_data->sink) { +                pa_log_info("Restoring device for stream %s.", name); +                new_data->sink = s; +            } else +                pa_log_info("Not restore device for stream %s, because already set.", name);          }          pa_xfree(e); @@ -330,13 +333,20 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu      if ((e = read_entry(u, name))) {          if (u->restore_volume) { -            pa_log_info("Restoring volume for sink input %s.", name); -            pa_sink_input_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map)); + +            if (!new_data->volume_is_set) { +                pa_log_info("Restoring volume for sink input %s.", name); +                pa_sink_input_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map)); +            } else +                pa_log_debug("Not restoring volume for sink input %s, because already set.", name);          }          if (u->restore_muted) { -            pa_log_info("Restoring mute state for sink input %s.", name); -            pa_sink_input_new_data_set_muted(new_data, e->muted); +            if (!new_data->muted_is_set) { +                pa_log_info("Restoring mute state for sink input %s.", name); +                pa_sink_input_new_data_set_muted(new_data, e->muted); +            } else +                pa_log_debug("Not restoring mute state for sink input %s, because already set.", name);          }          pa_xfree(e); @@ -360,10 +370,14 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou          pa_source *s;          if (u->restore_device && +            !new_data->direct_on_input &&              (s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE, TRUE))) { -            pa_log_info("Restoring device for stream %s.", name); -            new_data->source = s; +            if (!new_data->source) { +                pa_log_info("Restoring device for stream %s.", name); +                new_data->source = s; +            } else +                pa_log_info("Not restoring device for stream %s, because already set", name);          }          pa_xfree(e); @@ -408,7 +422,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {          char *n;          pa_sink *s; -        if (!(n = get_name(si->proplist, "sink_input"))) +        if (!(n = get_name(si->proplist, "sink-input")))              continue;          if (strcmp(name, n)) { @@ -417,8 +431,9 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {          }          if (u->restore_volume) { +            pa_cvolume v = e->volume;              pa_log_info("Restoring volume for sink input %s.", name); -            pa_sink_input_set_volume(si, pa_cvolume_remap(&e->volume, &e->channel_map, &si->channel_map)); +            pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map));          }          if (u->restore_muted) { @@ -490,7 +505,7 @@ static void dump_database(struct userdata *u) {  static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {      struct userdata *u;      uint32_t command; -    pa_tagstruct *reply; +    pa_tagstruct *reply = NULL;      pa_assert(p);      pa_assert(m); @@ -529,7 +544,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio                  next_key = gdbm_nextkey(u->gdbm_file, key); -                name = pa_xstrndup(key.dptr, key.dsize); +                name = pa_xstrndup(key.dptr, (size_t) key.dsize);                  pa_xfree(key.dptr);                  if ((e = read_entry(u, name))) { @@ -552,7 +567,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio          case SUBCOMMAND_WRITE: {              uint32_t mode; -            pa_bool_t apply_immediately; +            pa_bool_t apply_immediately = FALSE;              if (pa_tagstruct_getu32(t, &mode) < 0 ||                  pa_tagstruct_get_boolean(t, &apply_immediately) < 0) @@ -571,6 +586,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio                  pa_bool_t muted;                  struct entry entry;                  datum key, data; +                int k;                  memset(&entry, 0, sizeof(entry)); @@ -588,12 +604,12 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio                  pa_strlcpy(entry.device, device, sizeof(entry.device));                  key.dptr = (void*) name; -                key.dsize = strlen(name); +                key.dsize = (int) strlen(name);                  data.dptr = (void*) &entry;                  data.dsize = sizeof(entry); -                if (gdbm_store(u->gdbm_file, key, data, mode == PA_UPDATE_REPLACE ? GDBM_REPLACE : GDBM_INSERT) == 1) +                if ((k = gdbm_store(u->gdbm_file, key, data, mode == PA_UPDATE_REPLACE ? GDBM_REPLACE : GDBM_INSERT)) == 0)                      if (apply_immediately)                          apply_entry(u, name, &entry);              } @@ -613,7 +629,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio                      goto fail;                  key.dptr = (void*) name; -                key.dsize = strlen(name); +                key.dsize = (int) strlen(name);                  gdbm_delete(u->gdbm_file, key);              } @@ -670,6 +686,7 @@ int pa__init(pa_module*m) {      pa_source_output *so;      uint32_t idx;      pa_bool_t restore_device = TRUE, restore_volume = TRUE, restore_muted = TRUE; +    int gdbm_cache_size;      pa_assert(m); @@ -730,6 +747,10 @@ int pa__init(pa_module*m) {          goto fail;      } +    /* By default the cache of gdbm is rather large, let's reduce it a bit to save memory */ +    gdbm_cache_size = 10; +    gdbm_setopt(u->gdbm_file, GDBM_CACHESIZE, &gdbm_cache_size, sizeof(gdbm_cache_size)); +      pa_log_info("Sucessfully opened database file '%s'.", fname);      pa_xfree(fname); diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c index bc7c023c..8ab84e08 100644 --- a/src/modules/module-suspend-on-idle.c +++ b/src/modules/module-suspend-on-idle.c @@ -83,12 +83,12 @@ static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval      d->userdata->core->mainloop->time_restart(d->time_event, NULL); -    if (d->sink && pa_sink_used_by(d->sink) <= 0 && pa_sink_get_state(d->sink) != PA_SINK_SUSPENDED) { +    if (d->sink && pa_sink_check_suspend(d->sink) <= 0 && pa_sink_get_state(d->sink) != PA_SINK_SUSPENDED) {          pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name);          pa_sink_suspend(d->sink, TRUE);      } -    if (d->source && pa_source_used_by(d->source) <= 0 && pa_source_get_state(d->source) != PA_SOURCE_SUSPENDED) { +    if (d->source && pa_source_check_suspend(d->source) <= 0 && pa_source_get_state(d->source) != PA_SOURCE_SUSPENDED) {          pa_log_info("Source %s idle for too long, suspending ...", d->source->name);          pa_source_suspend(d->source, TRUE);      } @@ -158,7 +158,7 @@ static pa_hook_result_t sink_input_unlink_hook_cb(pa_core *c, pa_sink_input *s,      pa_sink_input_assert_ref(s);      pa_assert(u); -    if (pa_sink_used_by(s->sink) <= 0) { +    if (pa_sink_check_suspend(s->sink) <= 0) {          struct device_info *d;          if ((d = pa_hashmap_get(u->device_infos, s->sink)))              restart(d); @@ -172,7 +172,7 @@ static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_outpu      pa_source_output_assert_ref(s);      pa_assert(u); -    if (pa_source_used_by(s->source) <= 0) { +    if (pa_source_check_suspend(s->source) <= 0) {          struct device_info *d;          if ((d = pa_hashmap_get(u->device_infos, s->source)))              restart(d); @@ -191,7 +191,7 @@ static pa_hook_result_t sink_input_move_hook_cb(pa_core *c, pa_sink_input_move_h      if ((d = pa_hashmap_get(u->device_infos, data->destination)))          resume(d); -    if (pa_sink_used_by(data->sink_input->sink) <= 1) +    if (pa_sink_check_suspend(data->sink_input->sink) <= 1)          if ((d = pa_hashmap_get(u->device_infos, data->sink_input->sink)))              restart(d); @@ -208,7 +208,7 @@ static pa_hook_result_t source_output_move_hook_cb(pa_core *c, pa_source_output_      if ((d = pa_hashmap_get(u->device_infos, data->destination)))          resume(d); -    if (pa_source_used_by(data->source_output->source) <= 1) +    if (pa_source_check_suspend(data->source_output->source) <= 1)          if ((d = pa_hashmap_get(u->device_infos, data->source_output->source)))              restart(d); @@ -266,8 +266,8 @@ static pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct user      d->time_event = c->mainloop->time_new(c->mainloop, NULL, timeout_cb, d);      pa_hashmap_put(u->device_infos, o, d); -    if ((d->sink && pa_sink_used_by(d->sink) <= 0) || -        (d->source && pa_source_used_by(d->source) <= 0)) +    if ((d->sink && pa_sink_check_suspend(d->sink) <= 0) || +        (d->source && pa_source_check_suspend(d->source) <= 0))          restart(d);      return PA_HOOK_OK; @@ -313,7 +313,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s          pa_sink *s = PA_SINK(o);          pa_sink_state_t state = pa_sink_get_state(s); -        if (pa_sink_used_by(s) <= 0) { +        if (pa_sink_check_suspend(s) <= 0) {              if (PA_SINK_IS_OPENED(state))                  restart(d); @@ -324,7 +324,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s          pa_source *s = PA_SOURCE(o);          pa_source_state_t state = pa_source_get_state(s); -        if (pa_source_used_by(s) <= 0) { +        if (pa_source_check_suspend(s) <= 0) {              if (PA_SOURCE_IS_OPENED(state))                  restart(d); @@ -337,7 +337,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s  int pa__init(pa_module*m) {      pa_modargs *ma = NULL;      struct userdata *u; -    uint32_t timeout = 1; +    uint32_t timeout = 5;      uint32_t idx;      pa_sink *sink;      pa_source *source; diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index 79ce1dd2..a46d6e59 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -159,7 +159,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {      [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = command_suspended,      [PA_COMMAND_RECORD_STREAM_SUSPENDED] = command_suspended,      [PA_COMMAND_PLAYBACK_STREAM_MOVED] = command_moved, -    [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved, +    [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved  };  struct userdata { @@ -178,7 +178,7 @@ struct userdata {  #ifdef TUNNEL_SINK      char *sink_name;      pa_sink *sink; -    int32_t requested_bytes; +    size_t requested_bytes;  #else      char *source_name;      pa_source *source; @@ -389,7 +389,7 @@ static void send_data(struct userdata *u) {          u->requested_bytes -= memchunk.length; -        u->counter += memchunk.length; +        u->counter += (int64_t) memchunk.length;      }  } @@ -417,7 +417,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse          case PA_SINK_MESSAGE_GET_LATENCY: {              pa_usec_t yl, yr, *usec = data; -            yl = pa_bytes_to_usec(u->counter, &u->sink->sample_spec); +            yl = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);              yr = pa_smoother_get(u->smoother, pa_rtclock_usec());              *usec = yl > yr ? yl - yr : 0; @@ -444,10 +444,10 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse          case SINK_MESSAGE_UPDATE_LATENCY: {              pa_usec_t y; -            y = pa_bytes_to_usec(u->counter, &u->sink->sample_spec); +            y = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);              if (y > (pa_usec_t) offset || offset < 0) -                y -= offset; +                y -= (pa_usec_t) offset;              else                  y = 0; @@ -465,7 +465,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse              pa_pstream_send_memblock(u->pstream, u->channel, 0, PA_SEEK_RELATIVE, chunk); -            u->counter_delta += chunk->length; +            u->counter_delta += (int64_t) chunk->length;              return 0;      } @@ -508,7 +508,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off      switch (code) { -        case PA_SINK_MESSAGE_SET_STATE: { +        case PA_SOURCE_MESSAGE_SET_STATE: {              int r;              if ((r = pa_source_process_msg(o, code, data, offset, chunk)) >= 0) @@ -520,7 +520,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off          case PA_SOURCE_MESSAGE_GET_LATENCY: {              pa_usec_t yr, yl, *usec = data; -            yl = pa_bytes_to_usec(u->counter, &PA_SINK(o)->sample_spec); +            yl = pa_bytes_to_usec((uint64_t) u->counter, &PA_SOURCE(o)->sample_spec);              yr = pa_smoother_get(u->smoother, pa_rtclock_usec());              *usec = yr > yl ? yr - yl : 0; @@ -532,7 +532,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off              if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))                  pa_source_post(u->source, chunk); -            u->counter += chunk->length; +            u->counter += (int64_t) chunk->length;              return 0; @@ -544,10 +544,10 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off          case SOURCE_MESSAGE_UPDATE_LATENCY: {              pa_usec_t y; -            y = pa_bytes_to_usec(u->counter, &u->source->sample_spec); +            y = pa_bytes_to_usec((uint64_t) u->counter, &u->source->sample_spec);              if (offset >= 0 || y > (pa_usec_t) -offset) -                y += offset; +                y += (pa_usec_t) offset;              else                  y = 0; @@ -736,9 +736,9 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint      /* Add the length of our server-side buffer */      if (write_index >= read_index) -        delay += (int64_t) pa_bytes_to_usec(write_index-read_index, ss); +        delay += (int64_t) pa_bytes_to_usec((uint64_t) (write_index-read_index), ss);      else -        delay -= (int64_t) pa_bytes_to_usec(read_index-write_index, ss); +        delay -= (int64_t) pa_bytes_to_usec((uint64_t) (read_index-write_index), ss);      /* Our measurements are already out of date, hence correct by the     *       * transport latency */ @@ -750,9 +750,9 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint      /* Now correct by what we have have read/written since we requested the update */  #ifdef TUNNEL_SINK -    delay += (int64_t) pa_bytes_to_usec(u->counter_delta, ss); +    delay += (int64_t) pa_bytes_to_usec((uint64_t) u->counter_delta, ss);  #else -    delay -= (int64_t) pa_bytes_to_usec(u->counter_delta, ss); +    delay -= (int64_t) pa_bytes_to_usec((uint64_t) u->counter_delta, ss);  #endif  #ifdef TUNNEL_SINK @@ -1425,11 +1425,11 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t          u->maxlength = 4*1024*1024;  #ifdef TUNNEL_SINK -    u->tlength = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_TLENGTH_MSEC, &u->sink->sample_spec); -    u->minreq = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_MINREQ_MSEC, &u->sink->sample_spec); +    u->tlength = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_TLENGTH_MSEC, &u->sink->sample_spec); +    u->minreq = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_MINREQ_MSEC, &u->sink->sample_spec);      u->prebuf = u->tlength;  #else -    u->fragsize = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_FRAGSIZE_MSEC, &u->source->sample_spec); +    u->fragsize = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_FRAGSIZE_MSEC, &u->source->sample_spec);  #endif  #ifdef TUNNEL_SINK @@ -1494,6 +1494,13 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t  #endif      } +    if (u->version >= 14) { +#ifdef TUNNEL_SINK +        pa_tagstruct_put_boolean(reply, FALSE); /* volume_set */ +#endif +        pa_tagstruct_put_boolean(reply, TRUE); /* early rquests */ +    } +      pa_pstream_send_tagstruct(u->pstream, reply);      pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL); @@ -1548,7 +1555,7 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o      pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, PA_UINT_TO_PTR(seek), offset, chunk); -    u->counter_delta += chunk->length; +    u->counter_delta += (int64_t) chunk->length;  }  #endif diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c index 0fb17a0d..aac0d046 100644 --- a/src/modules/module-volume-restore.c +++ b/src/modules/module-volume-restore.c @@ -103,7 +103,7 @@ static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) {      if (k <= 0 || k > (long) PA_CHANNELS_MAX)          return NULL; -    v->channels = (unsigned) k; +    v->channels = (uint8_t) k;      for (i = 0; i < v->channels; i++) {          p += strspn(p, WHITESPACE); diff --git a/src/modules/module-x11-bell.c b/src/modules/module-x11-bell.c index ae16b9ae..e93721c1 100644 --- a/src/modules/module-x11-bell.c +++ b/src/modules/module-x11-bell.c @@ -82,7 +82,7 @@ static int x11_event_cb(pa_x11_wrapper *w, XEvent *e, void *userdata) {      bne = (XkbBellNotifyEvent*) e; -    if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, TRUE, (bne->percent*PA_VOLUME_NORM)/100, NULL, NULL) < 0) { +    if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, TRUE, ((pa_volume_t) bne->percent*PA_VOLUME_NORM)/100U, NULL, NULL) < 0) {          pa_log_info("Ringing bell failed, reverting to X11 device bell.");          XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent);      } diff --git a/src/modules/module-x11-xsmp.c b/src/modules/module-x11-xsmp.c index 12e100b7..57d182fd 100644 --- a/src/modules/module-x11-xsmp.c +++ b/src/modules/module-x11-xsmp.c @@ -181,7 +181,7 @@ int pa__init(pa_module*m) {      prop_program.name = (char*) SmProgram;      prop_program.type = (char*) SmARRAY8;      val_program.value = (char*) PACKAGE_NAME; -    val_program.length = strlen(val_program.value); +    val_program.length = (int) strlen(val_program.value);      prop_program.num_vals = 1;      prop_program.vals = &val_program;      prop_list[0] = &prop_program; @@ -190,7 +190,7 @@ int pa__init(pa_module*m) {      prop_user.type = (char*) SmARRAY8;      pa_get_user_name(t, sizeof(t));      val_user.value = t; -    val_user.length = strlen(val_user.value); +    val_user.length = (int) strlen(val_user.value);      prop_user.num_vals = 1;      prop_user.vals = &val_user;      prop_list[1] = &prop_user; diff --git a/src/modules/module-zeroconf-discover.c b/src/modules/module-zeroconf-discover.c index a4fbf020..c8087abb 100644 --- a/src/modules/module-zeroconf-discover.c +++ b/src/modules/module-zeroconf-discover.c @@ -173,9 +173,9 @@ static void resolver_cb(                  device = value;                  value = NULL;              } else if (strcmp(key, "rate") == 0) -                ss.rate = atoi(value); +                ss.rate = (uint32_t) atoi(value);              else if (strcmp(key, "channels") == 0) -                ss.channels = atoi(value); +                ss.channels = (uint8_t) atoi(value);              else if (strcmp(key, "format") == 0)                  ss.format = pa_parse_sample_format(value);              else if (strcmp(key, "channel_map") == 0) { diff --git a/src/modules/oss-util.c b/src/modules/oss-util.c index 2791e165..f766030d 100644 --- a/src/modules/oss-util.c +++ b/src/modules/oss-util.c @@ -204,10 +204,10 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {      if (ss->channels != channels) {          pa_log_warn("device doesn't support %i channels, using %i channels.", ss->channels, channels); -        ss->channels = channels; +        ss->channels = (uint8_t) channels;      } -    speed = ss->rate; +    speed = (int) ss->rate;      if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) {          pa_log("SNDCTL_DSP_SPEED: %s", pa_cstrerror(errno));          return -1; @@ -219,7 +219,7 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {          /* If the sample rate deviates too much, we need to resample */          if (speed < ss->rate*.95 || speed > ss->rate*1.05) -            ss->rate = speed; +            ss->rate = (uint32_t) speed;      }      return 0; diff --git a/src/modules/raop/base64.c b/src/modules/raop/base64.c new file mode 100644 index 00000000..8918def8 --- /dev/null +++ b/src/modules/raop/base64.c @@ -0,0 +1,126 @@ +/*** +  This file is part of PulseAudio. + +  Copyright 2008 Colin Guthrie + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +/* +  This file was originally inspired by a file developed by +    Kungliga Tekniska H�gskolan +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> + +#include <pulse/xmalloc.h> + +#include "base64.h" + +static const char base64_chars[] = +    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static int pos(char c) +{ +    if (c >= 'A' && c <= 'Z') return c - 'A' + 0; +    if (c >= 'a' && c <= 'z') return c - 'a' + 26; +    if (c >= '0' && c <= '9') return c - '0' + 52; +    if (c == '+') return 62; +    if (c == '/') return 63; +} + +int pa_base64_encode(const void *data, int size, char **str) +{ +    char *s, *p; +    int i; +    int c; +    const unsigned char *q; + +    p = s = pa_xnew(char, size * 4 / 3 + 4); +    q = (const unsigned char *) data; +    i = 0; +    for (i = 0; i < size;) { +        c = q[i++]; +        c *= 256; +        if (i < size) +            c += q[i]; +        i++; +        c *= 256; +        if (i < size) +            c += q[i]; +        i++; +        p[0] = base64_chars[(c & 0x00fc0000) >> 18]; +        p[1] = base64_chars[(c & 0x0003f000) >> 12]; +        p[2] = base64_chars[(c & 0x00000fc0) >> 6]; +        p[3] = base64_chars[(c & 0x0000003f) >> 0]; +        if (i > size) +            p[3] = '='; +        if (i > size + 1) +            p[2] = '='; +        p += 4; +    } +    *p = 0; +    *str = s; +    return strlen(s); +} + +#define DECODE_ERROR 0xffffffff + +static unsigned int token_decode(const char *token) +{ +    int i; +    unsigned int val = 0; +    int marker = 0; +    if (strlen(token) < 4) +        return DECODE_ERROR; +    for (i = 0; i < 4; i++) { +        val *= 64; +        if (token[i] == '=') +            marker++; +        else if (marker > 0) +            return DECODE_ERROR; +        else +            val += pos(token[i]); +    } +    if (marker > 2) +        return DECODE_ERROR; +    return (marker << 24) | val; +} + +int pa_base64_decode(const char *str, void *data) +{ +    const char *p; +    unsigned char *q; + +    q = data; +    for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) { +        unsigned int val = token_decode(p); +        unsigned int marker = (val >> 24) & 0xff; +        if (val == DECODE_ERROR) +            return -1; +        *q++ = (val >> 16) & 0xff; +        if (marker < 2) +            *q++ = (val >> 8) & 0xff; +        if (marker < 1) +            *q++ = val & 0xff; +    } +    return q - (unsigned char *) data; +} diff --git a/src/modules/raop/base64.h b/src/modules/raop/base64.h new file mode 100644 index 00000000..dac0e707 --- /dev/null +++ b/src/modules/raop/base64.h @@ -0,0 +1,34 @@ +#ifndef foobase64hfoo +#define foobase64hfoo + +/*** +  This file is part of PulseAudio. + +  Copyright 2008 Colin Guthrie +  Copyright Kungliga Tekniska Høgskolan + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +/* +  This file was originally inspired by a file developed by +    Kungliga Tekniska Høgskolan +*/ + +int pa_base64_encode(const void *data, int size, char **str); +int pa_base64_decode(const char *str, void *data); + +#endif diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c new file mode 100644 index 00000000..4627545e --- /dev/null +++ b/src/modules/raop/raop_client.c @@ -0,0 +1,561 @@ +/*** +  This file is part of PulseAudio. + +  Copyright 2008 Colin Guthrie + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <sys/ioctl.h> + +#ifdef HAVE_SYS_FILIO_H +#include <sys/filio.h> +#endif + +/* TODO: Replace OpenSSL with NSS */ +#include <openssl/err.h> +#include <openssl/rand.h> +#include <openssl/aes.h> +#include <openssl/rsa.h> +#include <openssl/engine.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/core-util.h> +#include <pulsecore/socket-util.h> +#include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/strbuf.h> +#include <pulsecore/random.h> +#include <pulsecore/poll.h> + +#include "raop_client.h" +#include "rtsp_client.h" +#include "base64.h" + +#define AES_CHUNKSIZE 16 + +#define JACK_STATUS_DISCONNECTED 0 +#define JACK_STATUS_CONNECTED 1 + +#define JACK_TYPE_ANALOG 0 +#define JACK_TYPE_DIGITAL 1 + +#define VOLUME_DEF -30 +#define VOLUME_MIN -144 +#define VOLUME_MAX 0 + + +struct pa_raop_client { +    pa_core *core; +    char *host; +    char *sid; +    pa_rtsp_client *rtsp; + +    uint8_t jack_type; +    uint8_t jack_status; + +    /* Encryption Related bits */ +    AES_KEY aes; +    uint8_t aes_iv[AES_CHUNKSIZE]; /* initialization vector for aes-cbc */ +    uint8_t aes_nv[AES_CHUNKSIZE]; /* next vector for aes-cbc */ +    uint8_t aes_key[AES_CHUNKSIZE]; /* key for aes-cbc */ + +    pa_socket_client *sc; +    int fd; + +    uint16_t seq; +    uint32_t rtptime; + +    pa_raop_client_cb_t callback; +    void* userdata; +    pa_raop_client_closed_cb_t closed_callback; +    void* closed_userdata; +}; + +/** + * Function to write bits into a buffer. + * @param buffer Handle to the buffer. It will be incremented if new data requires it. + * @param bit_pos A pointer to a position buffer to keep track the current write location (0 for MSB, 7 for LSB) + * @param size A pointer to the byte size currently written. This allows the calling function to do simple buffer overflow checks + * @param data The data to write + * @param data_bit_len The number of bits from data to write + */ +static inline void bit_writer(uint8_t **buffer, uint8_t *bit_pos, int *size, uint8_t data, uint8_t data_bit_len) { +    int bits_left, bit_overflow; +    uint8_t bit_data; + +    if (!data_bit_len) +        return; + +    /* If bit pos is zero, we will definatly use at least one bit from the current byte so size increments. */ +    if (!*bit_pos) +        *size += 1; + +    /* Calc the number of bits left in the current byte of buffer */ +    bits_left = 7 - *bit_pos  + 1; +    /* Calc the overflow of bits in relation to how much space we have left... */ +    bit_overflow = bits_left - data_bit_len; +    if (bit_overflow >= 0) { +        /* We can fit the new data in our current byte */ +        /* As we write from MSB->LSB we need to left shift by the overflow amount */ +        bit_data = data << bit_overflow; +        if (*bit_pos) +            **buffer |= bit_data; +        else +            **buffer = bit_data; +        /* If our data fits exactly into the current byte, we need to increment our pointer */ +        if (0 == bit_overflow) { +            /* Do not increment size as it will be incremeneted on next call as bit_pos is zero */ +            *buffer += 1; +            *bit_pos = 0; +        } else { +            *bit_pos += data_bit_len; +        } +    } else { +        /* bit_overflow is negative, there for we will need a new byte from our buffer */ +        /* Firstly fill up what's left in the current byte */ +        bit_data = data >> -bit_overflow; +        **buffer |= bit_data; +        /* Increment our buffer pointer and size counter*/ +        *buffer += 1; +        *size += 1; +        **buffer = data << (8 + bit_overflow); +        *bit_pos = -bit_overflow; +    } +} + +static int rsa_encrypt(uint8_t *text, int len, uint8_t *res) { +    const char n[] = +        "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC" +        "5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDR" +        "KSKv6kDqnw4UwPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuB" +        "OitnZ/bDzPHrTOZz0Dew0uowxf/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJ" +        "Q+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/UAaHqn9JdsBWLUEpVviYnh" +        "imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew=="; +    const char e[] = "AQAB"; +    uint8_t modules[256]; +    uint8_t exponent[8]; +    int size; +    RSA *rsa; + +    rsa = RSA_new(); +    size = pa_base64_decode(n, modules); +    rsa->n = BN_bin2bn(modules, size, NULL); +    size = pa_base64_decode(e, exponent); +    rsa->e = BN_bin2bn(exponent, size, NULL); + +    size = RSA_public_encrypt(len, text, res, rsa, RSA_PKCS1_OAEP_PADDING); +    RSA_free(rsa); +    return size; +} + +static int aes_encrypt(pa_raop_client* c, uint8_t *data, int size) +{ +    uint8_t *buf; +    int i=0, j; + +    pa_assert(c); + +    memcpy(c->aes_nv, c->aes_iv, AES_CHUNKSIZE); +    while (i+AES_CHUNKSIZE <= size) { +        buf = data + i; +        for (j=0; j<AES_CHUNKSIZE; ++j) +            buf[j] ^= c->aes_nv[j]; + +        AES_encrypt(buf, buf, &c->aes); +        memcpy(c->aes_nv, buf, AES_CHUNKSIZE); +        i += AES_CHUNKSIZE; +    } +    return i; +} + +static inline void rtrimchar(char *str, char rc) +{ +    char *sp = str + strlen(str) - 1; +    while (sp >= str && *sp == rc) { +        *sp = '\0'; +        sp -= 1; +    } +} + +static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) { +    pa_raop_client *c = userdata; + +    pa_assert(sc); +    pa_assert(c); +    pa_assert(c->sc == sc); +    pa_assert(c->fd < 0); +    pa_assert(c->callback); + +    pa_socket_client_unref(c->sc); +    c->sc = NULL; + +    if (!io) { +        pa_log("Connection failed: %s", pa_cstrerror(errno)); +        return; +    } + +    c->fd = pa_iochannel_get_send_fd(io); + +    pa_iochannel_set_noclose(io, TRUE); +    pa_iochannel_socket_set_sndbuf(io, 1024); +    pa_iochannel_free(io); + +    pa_make_tcp_socket_low_delay(c->fd); + +    pa_log_debug("Connection established"); +    c->callback(c->fd, c->userdata); +} + +static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *userdata) +{ +    pa_raop_client* c = userdata; +    pa_assert(c); +    pa_assert(rtsp); +    pa_assert(rtsp == c->rtsp); + +    switch (state) { +        case STATE_CONNECT: { +            int i; +            uint8_t rsakey[512]; +            char *key, *iv, *sac, *sdp; +            uint16_t rand_data; +            const char *ip; +            char *url; + +            pa_log_debug("RAOP: CONNECTED"); +            ip = pa_rtsp_localip(c->rtsp); +            /* First of all set the url properly */ +            url = pa_sprintf_malloc("rtsp://%s/%s", ip, c->sid); +            pa_rtsp_set_url(c->rtsp, url); +            pa_xfree(url); + +            /* Now encrypt our aes_public key to send to the device */ +            i = rsa_encrypt(c->aes_key, AES_CHUNKSIZE, rsakey); +            pa_base64_encode(rsakey, i, &key); +            rtrimchar(key, '='); +            pa_base64_encode(c->aes_iv, AES_CHUNKSIZE, &iv); +            rtrimchar(iv, '='); + +            pa_random(&rand_data, sizeof(rand_data)); +            pa_base64_encode(&rand_data, AES_CHUNKSIZE, &sac); +            rtrimchar(sac, '='); +            pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac); +            sdp = pa_sprintf_malloc( +                "v=0\r\n" +                "o=iTunes %s 0 IN IP4 %s\r\n" +                "s=iTunes\r\n" +                "c=IN IP4 %s\r\n" +                "t=0 0\r\n" +                "m=audio 0 RTP/AVP 96\r\n" +                "a=rtpmap:96 AppleLossless\r\n" +                "a=fmtp:96 4096 0 16 40 10 14 2 255 0 0 44100\r\n" +                "a=rsaaeskey:%s\r\n" +                "a=aesiv:%s\r\n", +                c->sid, ip, c->host, key, iv); +            pa_rtsp_announce(c->rtsp, sdp); +            pa_xfree(key); +            pa_xfree(iv); +            pa_xfree(sac); +            pa_xfree(sdp); +            break; +        } + +        case STATE_ANNOUNCE: +            pa_log_debug("RAOP: ANNOUNCED"); +            pa_rtsp_remove_header(c->rtsp, "Apple-Challenge"); +            pa_rtsp_setup(c->rtsp); +            break; + +        case STATE_SETUP: { +            char *aj = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Jack-Status")); +            pa_log_debug("RAOP: SETUP"); +            if (aj) { +                char *token, *pc; +                char delimiters[] = ";"; +                const char* token_state = NULL; +                c->jack_type = JACK_TYPE_ANALOG; +                c->jack_status = JACK_STATUS_DISCONNECTED; + +                while ((token = pa_split(aj, delimiters, &token_state))) { +                    if ((pc = strstr(token, "="))) { +                      *pc = 0; +                      if (!strcmp(token, "type") && !strcmp(pc+1, "digital")) { +                          c->jack_type = JACK_TYPE_DIGITAL; +                      } +                    } else { +                        if (!strcmp(token,"connected")) +                            c->jack_status = JACK_STATUS_CONNECTED; +                    } +                    pa_xfree(token); +                } +                pa_xfree(aj); +            } else { +                pa_log_warn("Audio Jack Status missing"); +            } +            pa_rtsp_record(c->rtsp, &c->seq, &c->rtptime); +            break; +        } + +        case STATE_RECORD: { +            uint32_t port = pa_rtsp_serverport(c->rtsp); +            pa_log_debug("RAOP: RECORDED"); + +            if (!(c->sc = pa_socket_client_new_string(c->core->mainloop, c->host, port))) { +                pa_log("failed to connect to server '%s:%d'", c->host, port); +                return; +            } +            pa_socket_client_set_callback(c->sc, on_connection, c); +            break; +        } + +        case STATE_FLUSH: +            pa_log_debug("RAOP: FLUSHED"); +            break; + +        case STATE_TEARDOWN: +        case STATE_SET_PARAMETER: +            pa_log_debug("RAOP: SET_PARAMETER"); +            break; +        case STATE_DISCONNECTED: +            pa_assert(c->closed_callback); +            pa_assert(c->rtsp); + +            pa_log_debug("RTSP control channel closed"); +            pa_rtsp_client_free(c->rtsp); +            c->rtsp = NULL; +            if (c->fd > 0) { +                /* We do not close the fd, we leave it to the closed callback to do that */ +                c->fd = -1; +            } +            if (c->sc) { +                pa_socket_client_unref(c->sc); +                c->sc = NULL; +            } +            pa_xfree(c->sid); +            c->sid = NULL; +            c->closed_callback(c->closed_userdata); +            break; +    } +} + +pa_raop_client* pa_raop_client_new(pa_core *core, const char* host) +{ +    pa_raop_client* c = pa_xnew0(pa_raop_client, 1); + +    pa_assert(core); +    pa_assert(host); + +    c->core = core; +    c->fd = -1; +    c->host = pa_xstrdup(host); + +    if (pa_raop_connect(c)) { +        pa_raop_client_free(c); +        return NULL; +    } +    return c; +} + + +void pa_raop_client_free(pa_raop_client* c) +{ +    pa_assert(c); + +    if (c->rtsp) +        pa_rtsp_client_free(c->rtsp); +    pa_xfree(c->host); +    pa_xfree(c); +} + + +int pa_raop_connect(pa_raop_client* c) +{ +    char *sci; +    struct { +        uint32_t a; +        uint32_t b; +        uint32_t c; +    } rand_data; + +    pa_assert(c); + +    if (c->rtsp) { +        pa_log_debug("Connection already in progress"); +        return 0; +    } + +    c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, 5000, "iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)"); + +    /* Initialise the AES encryption system */ +    pa_random(c->aes_iv, sizeof(c->aes_iv)); +    pa_random(c->aes_key, sizeof(c->aes_key)); +    memcpy(c->aes_nv, c->aes_iv, sizeof(c->aes_nv)); +    AES_set_encrypt_key(c->aes_key, 128, &c->aes); + +    /* Generate random instance id */ +    pa_random(&rand_data, sizeof(rand_data)); +    c->sid = pa_sprintf_malloc("%u", rand_data.a); +    sci = pa_sprintf_malloc("%08x%08x",rand_data.b, rand_data.c); +    pa_rtsp_add_header(c->rtsp, "Client-Instance", sci); +    pa_xfree(sci); +    pa_rtsp_set_callback(c->rtsp, rtsp_cb, c); +    return pa_rtsp_connect(c->rtsp); +} + + +int pa_raop_flush(pa_raop_client* c) +{ +    pa_assert(c); + +    pa_rtsp_flush(c->rtsp, c->seq, c->rtptime); +    return 0; +} + + +int pa_raop_client_set_volume(pa_raop_client* c, pa_volume_t volume) +{ +    int rv; +    double db; +    char *param; + +    pa_assert(c); + +    db = pa_sw_volume_to_dB(volume); +    if (db < VOLUME_MIN) +        db = VOLUME_MIN; +    else if (db > VOLUME_MAX) +        db = VOLUME_MAX; + +    param = pa_sprintf_malloc("volume: %0.6f\r\n",  db); + +    /* We just hit and hope, cannot wait for the callback */ +    rv = pa_rtsp_setparameter(c->rtsp, param); +    pa_xfree(param); +    return rv; +} + + +int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchunk* encoded) +{ +    uint16_t len; +    size_t bufmax; +    uint8_t *bp, bpos; +    uint8_t *ibp, *maxibp; +    int size; +    uint8_t *b, *p; +    uint32_t bsize; +    size_t length; +    static uint8_t header[] = { +        0x24, 0x00, 0x00, 0x00, +        0xF0, 0xFF, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, +    }; +    int header_size = sizeof(header); + +    pa_assert(c); +    pa_assert(c->fd > 0); +    pa_assert(raw); +    pa_assert(raw->memblock); +    pa_assert(raw->length > 0); +    pa_assert(encoded); + +    /* We have to send 4 byte chunks */ +    bsize = (int)(raw->length / 4); +    length = bsize * 4; + +    /* Leave 16 bytes extra to allow for the ALAC header which is about 55 bits */ +    bufmax = length + header_size + 16; +    pa_memchunk_reset(encoded); +    encoded->memblock = pa_memblock_new(c->core->mempool, bufmax); +    b = pa_memblock_acquire(encoded->memblock); +    memcpy(b, header, header_size); + +    /* Now write the actual samples */ +    bp = b + header_size; +    size = bpos = 0; +    bit_writer(&bp,&bpos,&size,1,3); /* channel=1, stereo */ +    bit_writer(&bp,&bpos,&size,0,4); /* unknown */ +    bit_writer(&bp,&bpos,&size,0,8); /* unknown */ +    bit_writer(&bp,&bpos,&size,0,4); /* unknown */ +    bit_writer(&bp,&bpos,&size,1,1); /* hassize */ +    bit_writer(&bp,&bpos,&size,0,2); /* unused */ +    bit_writer(&bp,&bpos,&size,1,1); /* is-not-compressed */ + +    /* size of data, integer, big endian */ +    bit_writer(&bp,&bpos,&size,(bsize>>24)&0xff,8); +    bit_writer(&bp,&bpos,&size,(bsize>>16)&0xff,8); +    bit_writer(&bp,&bpos,&size,(bsize>>8)&0xff,8); +    bit_writer(&bp,&bpos,&size,(bsize)&0xff,8); + +    ibp = p = pa_memblock_acquire(raw->memblock); +    maxibp = p + raw->length - 4; +    while (ibp <= maxibp) { +        /* Byte swap stereo data */ +        bit_writer(&bp,&bpos,&size,*(ibp+1),8); +        bit_writer(&bp,&bpos,&size,*(ibp+0),8); +        bit_writer(&bp,&bpos,&size,*(ibp+3),8); +        bit_writer(&bp,&bpos,&size,*(ibp+2),8); +        ibp += 4; +        raw->index += 4; +        raw->length -= 4; +    } +    pa_memblock_release(raw->memblock); +    encoded->length = header_size + size; + +    /* store the lenght (endian swapped: make this better) */ +    len = size + header_size - 4; +    *(b + 2) = len >> 8; +    *(b + 3) = len & 0xff; + +    /* encrypt our data */ +    aes_encrypt(c, (b + header_size), size); + +    /* We're done with the chunk */ +    pa_memblock_release(encoded->memblock); + +    return 0; +} + + +void pa_raop_client_set_callback(pa_raop_client* c, pa_raop_client_cb_t callback, void *userdata) +{ +    pa_assert(c); + +    c->callback = callback; +    c->userdata = userdata; +} + +void pa_raop_client_set_closed_callback(pa_raop_client* c, pa_raop_client_closed_cb_t callback, void *userdata) +{ +    pa_assert(c); + +    c->closed_callback = callback; +    c->closed_userdata = userdata; +} diff --git a/src/modules/raop/raop_client.h b/src/modules/raop/raop_client.h new file mode 100644 index 00000000..ec3136a7 --- /dev/null +++ b/src/modules/raop/raop_client.h @@ -0,0 +1,46 @@ +#ifndef fooraopclientfoo +#define fooraopclientfoo + +/*** +  This file is part of PulseAudio. + +  Copyright 2008 Colin Guthrie + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#include <pulse/mainloop-api.h> +#include <pulsecore/iochannel.h> +#include <pulsecore/core.h> + +typedef struct pa_raop_client pa_raop_client; + +pa_raop_client* pa_raop_client_new(pa_core *core, const char* host); +void pa_raop_client_free(pa_raop_client* c); + +int pa_raop_connect(pa_raop_client* c); +int pa_raop_flush(pa_raop_client* c); + +int pa_raop_client_set_volume(pa_raop_client* c, pa_volume_t volume); +int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchunk* encoded); + +typedef void (*pa_raop_client_cb_t)(int fd, void *userdata); +void pa_raop_client_set_callback(pa_raop_client* c, pa_raop_client_cb_t callback, void *userdata); + +typedef void (*pa_raop_client_closed_cb_t)(void *userdata); +void pa_raop_client_set_closed_callback(pa_raop_client* c, pa_raop_client_closed_cb_t callback, void *userdata); + +#endif diff --git a/src/modules/rtp/Makefile b/src/modules/rtp/Makefile index 316beb72..efe5a336 100644..120000 --- a/src/modules/rtp/Makefile +++ b/src/modules/rtp/Makefile @@ -1,13 +1 @@ -# This is a dirty trick just to ease compilation with emacs -# -# This file is not intended to be distributed or anything -# -# So: don't touch it, even better ignore it! - -all: -	$(MAKE) -C ../.. - -clean: -	$(MAKE) -C ../.. clean - -.PHONY: all clean +../../pulse/Makefile
\ No newline at end of file diff --git a/src/modules/rtp/headerlist.c b/src/modules/rtp/headerlist.c new file mode 100644 index 00000000..0fef835b --- /dev/null +++ b/src/modules/rtp/headerlist.c @@ -0,0 +1,186 @@ +/*** +  This file is part of PulseAudio. + +  Copyright 2008 Colin Guthrie +  Copyright 2007 Lennart Poettering + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as +  published by the Free Software Foundation; either version 2.1 of the +  License, or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  Lesser General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public +  License along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/hashmap.h> +#include <pulsecore/strbuf.h> +#include <pulsecore/core-util.h> + +#include "headerlist.h" + +struct header { +    char *key; +    void *value; +    size_t nbytes; +}; + +#define MAKE_HASHMAP(p) ((pa_hashmap*) (p)) +#define MAKE_HEADERLIST(p) ((pa_headerlist*) (p)) + +static void header_free(struct header *hdr) { +    pa_assert(hdr); + +    pa_xfree(hdr->key); +    pa_xfree(hdr->value); +    pa_xfree(hdr); +} + +pa_headerlist* pa_headerlist_new(void) { +    return MAKE_HEADERLIST(pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func)); +} + +void pa_headerlist_free(pa_headerlist* p) { +    struct header *hdr; + +    while ((hdr = pa_hashmap_steal_first(MAKE_HASHMAP(p)))) +        header_free(hdr); + +    pa_hashmap_free(MAKE_HASHMAP(p), NULL, NULL); +} + +int pa_headerlist_puts(pa_headerlist *p, const char *key, const char *value) { +    struct header *hdr; +    pa_bool_t add = FALSE; + +    pa_assert(p); +    pa_assert(key); + +    if (!(hdr = pa_hashmap_get(MAKE_HASHMAP(p), key))) { +        hdr = pa_xnew(struct header, 1); +        hdr->key = pa_xstrdup(key); +        add = TRUE; +    } else +        pa_xfree(hdr->value); + +    hdr->value = pa_xstrdup(value); +    hdr->nbytes = strlen(value)+1; + +    if (add) +        pa_hashmap_put(MAKE_HASHMAP(p), hdr->key, hdr); + +    return 0; +} + +int pa_headerlist_putsappend(pa_headerlist *p, const char *key, const char *value) { +    struct header *hdr; +    pa_bool_t add = FALSE; + +    pa_assert(p); +    pa_assert(key); + +    if (!(hdr = pa_hashmap_get(MAKE_HASHMAP(p), key))) { +        hdr = pa_xnew(struct header, 1); +        hdr->key = pa_xstrdup(key); +        hdr->value = pa_xstrdup(value); +        add = TRUE; +    } else { +        void *newval = pa_sprintf_malloc("%s%s", (char*)hdr->value, value); +        pa_xfree(hdr->value); +        hdr->value = newval; +    } +    hdr->nbytes = strlen(hdr->value)+1; + +    if (add) +        pa_hashmap_put(MAKE_HASHMAP(p), hdr->key, hdr); + +    return 0; +} + +const char *pa_headerlist_gets(pa_headerlist *p, const char *key) { +    struct header *hdr; + +    pa_assert(p); +    pa_assert(key); + +    if (!(hdr = pa_hashmap_get(MAKE_HASHMAP(p), key))) +        return NULL; + +    if (hdr->nbytes <= 0) +        return NULL; + +    if (((char*) hdr->value)[hdr->nbytes-1] != 0) +        return NULL; + +    if (strlen((char*) hdr->value) != hdr->nbytes-1) +        return NULL; + +    return (char*) hdr->value; +} + +int pa_headerlist_remove(pa_headerlist *p, const char *key) { +    struct header *hdr; + +    pa_assert(p); +    pa_assert(key); + +    if (!(hdr = pa_hashmap_remove(MAKE_HASHMAP(p), key))) +        return -1; + +    header_free(hdr); +    return 0; +} + +const char *pa_headerlist_iterate(pa_headerlist *p, void **state) { +    struct header *hdr; + +    if (!(hdr = pa_hashmap_iterate(MAKE_HASHMAP(p), state, NULL))) +        return NULL; + +    return hdr->key; +} + +char *pa_headerlist_to_string(pa_headerlist *p) { +    const char *key; +    void *state = NULL; +    pa_strbuf *buf; + +    pa_assert(p); + +    buf = pa_strbuf_new(); + +    while ((key = pa_headerlist_iterate(p, &state))) { + +        const char *v; + +        if ((v = pa_headerlist_gets(p, key))) +            pa_strbuf_printf(buf, "%s: %s\r\n", key, v); +    } + +    return pa_strbuf_tostring_free(buf); +} + +int pa_headerlist_contains(pa_headerlist *p, const char *key) { +    pa_assert(p); +    pa_assert(key); + +    if (!(pa_hashmap_get(MAKE_HASHMAP(p), key))) +        return 0; + +    return 1; +} diff --git a/src/modules/rtp/headerlist.h b/src/modules/rtp/headerlist.h new file mode 100644 index 00000000..4b9c6433 --- /dev/null +++ b/src/modules/rtp/headerlist.h @@ -0,0 +1,46 @@ +#ifndef foopulseheaderlisthfoo +#define foopulseheaderlisthfoo + +/*** +  This file is part of PulseAudio. + +  Copyright 2008 Colin Guthrie +  Copyright 2007 Lennart Poettering + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as +  published by the Free Software Foundation; either version 2.1 of the +  License, or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  Lesser General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public +  License along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#include <pulsecore/macro.h> + +typedef struct pa_headerlist pa_headerlist; + +pa_headerlist* pa_headerlist_new(void); +void pa_headerlist_free(pa_headerlist* p); + +int pa_headerlist_puts(pa_headerlist *p, const char *key, const char *value); +int pa_headerlist_putsappend(pa_headerlist *p, const char *key, const char *value); + +const char *pa_headerlist_gets(pa_headerlist *p, const char *key); + +int pa_headerlist_remove(pa_headerlist *p, const char *key); + +const char *pa_headerlist_iterate(pa_headerlist *p, void **state); + +char *pa_headerlist_to_string(pa_headerlist *p); + +int pa_headerlist_contains(pa_headerlist *p, const char *key); + +#endif diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c index e04e4611..e35773cc 100644 --- a/src/modules/rtp/module-rtp-recv.c +++ b/src/modules/rtp/module-rtp-recv.c @@ -238,25 +238,25 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {      else          delta = j; -    pa_memblockq_seek(s->memblockq, delta * s->rtp_context.frame_size, PA_SEEK_RELATIVE); +    pa_memblockq_seek(s->memblockq, delta * (int64_t) s->rtp_context.frame_size, PA_SEEK_RELATIVE);      pa_rtclock_get(&now); -    pa_smoother_put(s->smoother, pa_timeval_load(&now), pa_bytes_to_usec(pa_memblockq_get_write_index(s->memblockq), &s->sink_input->sample_spec)); +    pa_smoother_put(s->smoother, pa_timeval_load(&now), pa_bytes_to_usec((uint64_t) pa_memblockq_get_write_index(s->memblockq), &s->sink_input->sample_spec));      if (pa_memblockq_push(s->memblockq, &chunk) < 0) {          pa_log_warn("Queue overrun"); -        pa_memblockq_seek(s->memblockq, chunk.length, PA_SEEK_RELATIVE); +        pa_memblockq_seek(s->memblockq, (int64_t) chunk.length, PA_SEEK_RELATIVE);      } -    pa_log("blocks in q: %u", pa_memblockq_get_nblocks(s->memblockq)); +/*     pa_log("blocks in q: %u", pa_memblockq_get_nblocks(s->memblockq)); */      pa_memblock_unref(chunk.memblock);      /* The next timestamp we expect */ -    s->offset = s->rtp_context.timestamp + (chunk.length / s->rtp_context.frame_size); +    s->offset = s->rtp_context.timestamp + (uint32_t) (chunk.length / s->rtp_context.frame_size); -    pa_atomic_store(&s->timestamp, now.tv_sec); +    pa_atomic_store(&s->timestamp, (int) now.tv_sec);      if (s->last_rate_update + RATE_UPDATE_INTERVAL < pa_timeval_load(&now)) {          pa_usec_t wi, ri, render_delay, sink_delay = 0, latency, fix; @@ -265,7 +265,7 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {          pa_log("Updating sample rate");          wi = pa_smoother_get(s->smoother, pa_timeval_load(&now)); -        ri = pa_bytes_to_usec(pa_memblockq_get_read_index(s->memblockq), &s->sink_input->sample_spec); +        ri = pa_bytes_to_usec((uint64_t) pa_memblockq_get_read_index(s->memblockq), &s->sink_input->sample_spec);          if (PA_MSGOBJECT(s->sink_input->sink)->process_msg(PA_MSGOBJECT(s->sink_input->sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_delay, 0, NULL) < 0)              sink_delay = 0; @@ -291,7 +291,7 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {              fix = latency - s->intended_latency;          /* How many samples is this per second? */ -        fix_samples = fix * s->sink_input->thread_info.sample_spec.rate / RATE_UPDATE_INTERVAL; +        fix_samples = (unsigned) (fix * (pa_usec_t) s->sink_input->thread_info.sample_spec.rate / (pa_usec_t) RATE_UPDATE_INTERVAL);          /* Check if deviation is in bounds */          if (fix_samples > s->sink_input->sample_spec.rate*.20) @@ -431,7 +431,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in      s->smoother = pa_smoother_new(PA_USEC_PER_SEC*5, PA_USEC_PER_SEC*2, TRUE, 10);      pa_smoother_set_time_offset(s->smoother, pa_timeval_load(&now));      s->last_rate_update = pa_timeval_load(&now); -    pa_atomic_store(&s->timestamp, now.tv_sec); +    pa_atomic_store(&s->timestamp, (int) now.tv_sec);      if ((fd = mcast_socket((const struct sockaddr*) &sdp_info->sa, sdp_info->salen)) < 0)          goto fail; @@ -566,7 +566,7 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event          } else {              struct timeval now;              pa_rtclock_get(&now); -            pa_atomic_store(&s->timestamp, now.tv_sec); +            pa_atomic_store(&s->timestamp, (int) now.tv_sec);              pa_sdp_info_destroy(&info);          } diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c index 5e542253..8d1e92fe 100644 --- a/src/modules/rtp/module-rtp-send.c +++ b/src/modules/rtp/module-rtp-send.c @@ -67,10 +67,12 @@ PA_MODULE_USAGE(          "destination=<destination IP address> "          "port=<port number> "          "mtu=<maximum transfer unit> " -        "loop=<loopback to local host?>" +        "loop=<loopback to local host?> " +        "ttl=<ttl value>"  );  #define DEFAULT_PORT 46000 +#define DEFAULT_TTL 1  #define SAP_PORT 9875  #define DEFAULT_DESTINATION "224.0.0.56"  #define MEMBLOCKQ_MAXLENGTH (1024*170) @@ -86,6 +88,7 @@ static const char* const valid_modargs[] = {      "port",      "mtu" ,      "loop", +    "ttl",      NULL  }; @@ -167,7 +170,9 @@ int pa__init(pa_module*m) {      pa_modargs *ma = NULL;      const char *dest;      uint32_t port = DEFAULT_PORT, mtu; -    int af, fd = -1, sap_fd = -1; +    uint32_t ttl = DEFAULT_TTL; +    sa_family_t af; +    int fd = -1, sap_fd = -1;      pa_source *s;      pa_sample_spec ss;      pa_channel_map cm; @@ -219,14 +224,14 @@ int pa__init(pa_module*m) {      payload = pa_rtp_payload_from_sample_spec(&ss); -    mtu = pa_frame_align(DEFAULT_MTU, &ss); +    mtu = (uint32_t) pa_frame_align(DEFAULT_MTU, &ss);      if (pa_modargs_get_value_u32(ma, "mtu", &mtu) < 0 || mtu < 1 || mtu % pa_frame_size(&ss) != 0) {          pa_log("Invalid MTU.");          goto fail;      } -    port = DEFAULT_PORT + ((rand() % 512) << 1); +    port = DEFAULT_PORT + ((uint32_t) (rand() % 512) << 1);      if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) {          pa_log("port= expects a numerical argument between 1 and 65535.");          goto fail; @@ -235,16 +240,21 @@ int pa__init(pa_module*m) {      if (port & 1)          pa_log_warn("Port number not even as suggested in RFC3550!"); +    if (pa_modargs_get_value_u32(ma, "ttl", &ttl) < 0 || ttl < 1 || ttl > 0xFF) { +        pa_log("ttl= expects a numerical argument between 1 and 255."); +        goto fail; +    } +      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(port); +        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) {          sa4.sin_family = af = AF_INET; -        sa4.sin_port = htons(port); +        sa4.sin_port = htons((uint16_t) port);          sap_sa4 = sa4;          sap_sa4.sin_port = htons(SAP_PORT);      } else { @@ -257,7 +267,7 @@ int pa__init(pa_module*m) {          goto fail;      } -    if (connect(fd, af == AF_INET ? (struct sockaddr*) &sa4 : (struct sockaddr*) &sa6, af == AF_INET ? sizeof(sa4) : sizeof(sa6)) < 0) { +    if (connect(fd, af == AF_INET ? (struct sockaddr*) &sa4 : (struct sockaddr*) &sa6, (socklen_t) (af == AF_INET ? sizeof(sa4) : sizeof(sa6))) < 0) {          pa_log("connect() failed: %s", pa_cstrerror(errno));          goto fail;      } @@ -267,7 +277,7 @@ int pa__init(pa_module*m) {          goto fail;      } -    if (connect(sap_fd, af == AF_INET ? (struct sockaddr*) &sap_sa4 : (struct sockaddr*) &sap_sa6, af == AF_INET ? sizeof(sap_sa4) : sizeof(sap_sa6)) < 0) { +    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) {          pa_log("connect() failed: %s", pa_cstrerror(errno));          goto fail;      } @@ -279,6 +289,15 @@ int pa__init(pa_module*m) {          goto fail;      } +    if (ttl != DEFAULT_TTL) { +        int _ttl = (int) ttl; + +        if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &_ttl, sizeof(_ttl)) < 0) { +            pa_log("IP_MULTICAST_TTL failed: %s", pa_cstrerror(errno)); +            goto fail; +        } +    } +      /* If the socket queue is full, let's drop packets */      pa_make_fd_nonblock(fd);      pa_make_udp_socket_low_delay(fd); @@ -290,13 +309,14 @@ int pa__init(pa_module*m) {      pa_proplist_sets(data.proplist, "rtp.destination", dest);      pa_proplist_setf(data.proplist, "rtp.mtu", "%lu", (unsigned long) mtu);      pa_proplist_setf(data.proplist, "rtp.port", "%lu", (unsigned long) port); +    pa_proplist_setf(data.proplist, "rtp.ttl", "%lu", (unsigned long) ttl);      data.driver = __FILE__;      data.module = m;      data.source = s;      pa_source_output_new_data_set_sample_spec(&data, &ss);      pa_source_output_new_data_set_channel_map(&data, &cm); -    o = pa_source_output_new(m->core, &data, 0); +    o = pa_source_output_new(m->core, &data, PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND);      pa_source_output_new_data_done(&data);      if (!o) { @@ -335,14 +355,14 @@ int pa__init(pa_module*m) {      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, -                     n, port, payload, &ss); +                     n, (uint16_t) port, payload, &ss);      pa_xfree(n);      pa_rtp_context_init_send(&u->rtp_context, fd, m->core->cookie, payload, pa_frame_size(&ss));      pa_sap_context_init_send(&u->sap_context, sap_fd, p); -    pa_log_info("RTP stream initialized with mtu %u on %s:%u, SSRC=0x%08x, payload=%u, initial sequence #%u", mtu, dest, port, u->rtp_context.ssrc, payload, u->rtp_context.sequence); +    pa_log_info("RTP stream initialized with mtu %u on %s:%u ttl=%u, SSRC=0x%08x, payload=%u, initial sequence #%u", mtu, dest, port, ttl, u->rtp_context.ssrc, payload, u->rtp_context.sequence);      pa_log_info("SDP-Data:\n%s\nEOF", p);      pa_sap_send(&u->sap_context, 0); diff --git a/src/modules/rtp/rtp.c b/src/modules/rtp/rtp.c index 5a33ebc2..88351010 100644 --- a/src/modules/rtp/rtp.c +++ b/src/modules/rtp/rtp.c @@ -50,7 +50,7 @@ pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssr      c->sequence = (uint16_t) (rand()*rand());      c->timestamp = 0;      c->ssrc = ssrc ? ssrc : (uint32_t) (rand()*rand()); -    c->payload = payload & 127; +    c->payload = (uint8_t) (payload & 127U);      c->frame_size = frame_size;      pa_memchunk_reset(&c->memchunk); @@ -99,7 +99,8 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {          if (r < 0 || n >= size || iov_idx >= MAX_IOVECS) {              uint32_t header[3];              struct msghdr m; -            int k, i; +            ssize_t k; +            int i;              if (n > 0) {                  header[0] = htonl(((uint32_t) 2 << 30) | ((uint32_t) c->payload << 16) | ((uint32_t) c->sequence)); @@ -112,7 +113,7 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {                  m.msg_name = NULL;                  m.msg_namelen = 0;                  m.msg_iov = iov; -                m.msg_iovlen = iov_idx; +                m.msg_iovlen = (size_t) iov_idx;                  m.msg_control = NULL;                  m.msg_controllen = 0;                  m.msg_flags = 0; @@ -128,7 +129,7 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {              } else                  k = 0; -            c->timestamp += n/c->frame_size; +            c->timestamp += (unsigned) (n/c->frame_size);              if (k < 0) {                  if (errno != EAGAIN && errno != EINTR) /* If the queue is full, just ignore it */ @@ -162,7 +163,7 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {      struct msghdr m;      struct iovec iov;      uint32_t header; -    int cc; +    unsigned cc;      ssize_t r;      pa_assert(c); @@ -197,7 +198,7 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {      chunk->index = c->memchunk.index;      iov.iov_base = (uint8_t*) pa_memblock_acquire(chunk->memblock) + chunk->index; -    iov.iov_len = size; +    iov.iov_len = (size_t) size;      m.msg_name = NULL;      m.msg_namelen = 0; @@ -246,16 +247,16 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {      }      cc = (header >> 24) & 0xF; -    c->payload = (header >> 16) & 127; -    c->sequence = header & 0xFFFF; +    c->payload = (uint8_t) ((header >> 16) & 127U); +    c->sequence = (uint16_t) (header & 0xFFFFU); -    if (12 + cc*4 > size) { +    if (12 + cc*4 > (unsigned) size) {          pa_log_warn("RTP packet too short. (CSRC)");          goto fail;      }      chunk->index += 12 + cc*4; -    chunk->length = size - 12 + cc*4; +    chunk->length = (size_t) size - 12 + cc*4;      if (chunk->length % c->frame_size != 0) {          pa_log_warn("Bad RTP packet size."); diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c new file mode 100644 index 00000000..9eb3d964 --- /dev/null +++ b/src/modules/rtp/rtsp_client.c @@ -0,0 +1,542 @@ +/*** +  This file is part of PulseAudio. + +  Copyright 2008 Colin Guthrie + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <sys/ioctl.h> + +#ifdef HAVE_SYS_FILIO_H +#include <sys/filio.h> +#endif + +#include <pulse/xmalloc.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/core-util.h> +#include <pulsecore/socket-util.h> +#include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/strbuf.h> +#include <pulsecore/poll.h> +#include <pulsecore/ioline.h> + +#include "rtsp_client.h" + +struct pa_rtsp_client { +    pa_mainloop_api *mainloop; +    char *hostname; +    uint16_t port; + +    pa_socket_client *sc; +    pa_iochannel *io; +    pa_ioline *ioline; + +    pa_rtsp_cb_t callback; + +    void *userdata; +    const char *useragent; + +    pa_rtsp_state state; +    uint8_t waiting; + +    pa_headerlist* headers; +    char *last_header; +    pa_strbuf *header_buffer; +    pa_headerlist* response_headers; + +    char *localip; +    char *url; +    uint16_t rtp_port; +    uint32_t cseq; +    char *session; +    char *transport; +}; + +pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char* hostname, uint16_t port, const char* useragent) { +    pa_rtsp_client *c; + +    pa_assert(mainloop); +    pa_assert(hostname); +    pa_assert(port > 0); + +    c = pa_xnew0(pa_rtsp_client, 1); +    c->mainloop = mainloop; +    c->hostname = pa_xstrdup(hostname); +    c->port = port; +    c->headers = pa_headerlist_new(); + +    if (useragent) +        c->useragent = useragent; +    else +        c->useragent = "PulseAudio RTSP Client"; + +    return c; +} + + +void pa_rtsp_client_free(pa_rtsp_client* c) { +    if (c) { +        if (c->sc) +            pa_socket_client_unref(c->sc); +        if (c->ioline) +            pa_ioline_close(c->ioline); +        else if (c->io) +            pa_iochannel_free(c->io); + +        pa_xfree(c->hostname); +        pa_xfree(c->url); +        pa_xfree(c->localip); +        pa_xfree(c->session); +        pa_xfree(c->transport); +        pa_xfree(c->last_header); +        if (c->header_buffer) +            pa_strbuf_free(c->header_buffer); +        if (c->response_headers) +            pa_headerlist_free(c->response_headers); +        pa_headerlist_free(c->headers); +    } +    pa_xfree(c); +} + + +static void headers_read(pa_rtsp_client *c) { +    char* token; +    char delimiters[] = ";"; + +    pa_assert(c); +    pa_assert(c->response_headers); +    pa_assert(c->callback); + +    /* Deal with a SETUP response */ +    if (STATE_SETUP == c->state) { +        const char* token_state = NULL; +        const char* pc = NULL; +        c->session = pa_xstrdup(pa_headerlist_gets(c->response_headers, "Session")); +        c->transport = pa_xstrdup(pa_headerlist_gets(c->response_headers, "Transport")); + +        if (!c->session || !c->transport) { +            pa_headerlist_free(c->response_headers); +            c->response_headers = NULL; +            pa_log("Invalid SETUP response."); +            return; +        } + +        /* Now parse out the server port component of the response. */ +        while ((token = pa_split(c->transport, delimiters, &token_state))) { +            if ((pc = strstr(token, "="))) { +                if (0 == strncmp(token, "server_port", 11)) { +                    pa_atou(pc+1, (uint32_t*)(&c->rtp_port)); +                    pa_xfree(token); +                    break; +                } +            } +            pa_xfree(token); +        } +        if (0 == c->rtp_port) { +            /* Error no server_port in response */ +            pa_headerlist_free(c->response_headers); +            c->response_headers = NULL; +            pa_log("Invalid SETUP response (no port number)."); +            return; +        } +    } + +    /* Call our callback */ +    c->callback(c, c->state, c->response_headers, c->userdata); + +    pa_headerlist_free(c->response_headers); +    c->response_headers = NULL; +} + + +static void line_callback(pa_ioline *line, const char *s, void *userdata) { +    char *delimpos; +    char *s2, *s2p; + +    pa_rtsp_client *c = userdata; +    pa_assert(line); +    pa_assert(c); +    pa_assert(c->callback); + +    if (!s) { +        /* Keep the ioline/iochannel open as they will be freed automatically */ +        c->ioline = NULL; +        c->io = NULL; +        c->callback(c, STATE_DISCONNECTED, NULL, c->userdata); +        return; +    } + +    s2 = pa_xstrdup(s); +    /* Trim trailing carriage returns */ +    s2p = s2 + strlen(s2) - 1; +    while (s2p >= s2 && '\r' == *s2p) { +        *s2p = '\0'; +        s2p -= 1; +    } +    if (c->waiting && 0 == strcmp("RTSP/1.0 200 OK", s2)) { +        c->waiting = 0; +        pa_assert(!c->response_headers); +        c->response_headers = pa_headerlist_new(); +        goto exit; +    } +    if (c->waiting) { +        pa_log_warn("Unexpected response: %s", s2); +        goto exit;; +    } +    if (!strlen(s2)) { +        /* End of headers */ +        /* We will have a header left from our looping itteration, so add it in :) */ +        if (c->last_header) { +            /* This is not a continuation header so let's dump it into our proplist */ +            pa_headerlist_puts(c->response_headers, c->last_header, pa_strbuf_tostring_free(c->header_buffer)); +            pa_xfree(c->last_header); +            c->last_header = NULL; +            c->header_buffer= NULL; +        } + +        pa_log_debug("Full response received. Dispatching"); +        headers_read(c); +        c->waiting = 1; +        goto exit; +    } + +    /* Read and parse a header (we know it's not empty) */ +    /* TODO: Move header reading into the headerlist. */ + +    /* If the first character is a space, it's a continuation header */ +    if (c->last_header && ' ' == s2[0]) { +        pa_assert(c->header_buffer); + +        /* Add this line to the buffer (sans the space. */ +        pa_strbuf_puts(c->header_buffer, &(s2[1])); +        goto exit; +    } + +    if (c->last_header) { +        /* This is not a continuation header so let's dump the full +          header/value into our proplist */ +        pa_headerlist_puts(c->response_headers, c->last_header, pa_strbuf_tostring_free(c->header_buffer)); +        pa_xfree(c->last_header); +        c->last_header = NULL; +        c->header_buffer = NULL; +    } + +    delimpos = strstr(s2, ":"); +    if (!delimpos) { +        pa_log_warn("Unexpected response when expecting header: %s", s); +        goto exit; +    } + +    pa_assert(!c->header_buffer); +    pa_assert(!c->last_header); + +    c->header_buffer = pa_strbuf_new(); +    if (strlen(delimpos) > 1) { +        /* Cut our line off so we can copy the header name out */ +        *delimpos++ = '\0'; + +        /* Trim the front of any spaces */ +        while (' ' == *delimpos) +            ++delimpos; + +        pa_strbuf_puts(c->header_buffer, delimpos); +    } else { +        /* Cut our line off so we can copy the header name out */ +        *delimpos = '\0'; +    } + +    /* Save the header name */ +    c->last_header = pa_xstrdup(s2); +  exit: +    pa_xfree(s2); +} + + +static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) { +    pa_rtsp_client *c = userdata; +    union { +        struct sockaddr sa; +        struct sockaddr_in in; +        struct sockaddr_in6 in6; +    } sa; +    socklen_t sa_len = sizeof(sa); + +    pa_assert(sc); +    pa_assert(c); +    pa_assert(STATE_CONNECT == c->state); +    pa_assert(c->sc == sc); +    pa_socket_client_unref(c->sc); +    c->sc = NULL; + +    if (!io) { +        pa_log("Connection failed: %s", pa_cstrerror(errno)); +        return; +    } +    pa_assert(!c->io); +    c->io = io; + +    c->ioline = pa_ioline_new(io); +    pa_ioline_set_callback(c->ioline, line_callback, c); + +    /* Get the local IP address for use externally */ +    if (0 == getsockname(pa_iochannel_get_recv_fd(io), &sa.sa, &sa_len)) { +        char buf[INET6_ADDRSTRLEN]; +        const char *res = NULL; + +        if (AF_INET == sa.sa.sa_family) { +            if ((res = inet_ntop(sa.sa.sa_family, &sa.in.sin_addr, buf, sizeof(buf)))) { +                c->localip = pa_xstrdup(res); +            } +        } else if (AF_INET6 == sa.sa.sa_family) { +            if ((res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf)))) { +                c->localip = pa_sprintf_malloc("[%s]", res); +            } +        } +    } +    pa_log_debug("Established RTSP connection from local ip %s", c->localip); + +    if (c->callback) +        c->callback(c, c->state, NULL, c->userdata); +} + +int pa_rtsp_connect(pa_rtsp_client *c) { +    pa_assert(c); +    pa_assert(!c->sc); + +    pa_xfree(c->session); +    c->session = NULL; + +    if (!(c->sc = pa_socket_client_new_string(c->mainloop, c->hostname, c->port))) { +        pa_log("failed to connect to server '%s:%d'", c->hostname, c->port); +        return -1; +    } + +    pa_socket_client_set_callback(c->sc, on_connection, c); +    c->waiting = 1; +    c->state = STATE_CONNECT; +    return 0; +} + +void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userdata) { +    pa_assert(c); + +    c->callback = callback; +    c->userdata = userdata; +} + +void pa_rtsp_disconnect(pa_rtsp_client *c) { +    pa_assert(c); + +    if (c->io) +        pa_iochannel_free(c->io); +    c->io = NULL; +} + + +const char* pa_rtsp_localip(pa_rtsp_client* c) { +    pa_assert(c); + +    return c->localip; +} + +uint32_t pa_rtsp_serverport(pa_rtsp_client* c) { +    pa_assert(c); + +    return c->rtp_port; +} + +void pa_rtsp_set_url(pa_rtsp_client* c, const char* url) { +    pa_assert(c); + +    c->url = pa_xstrdup(url); +} + +void pa_rtsp_add_header(pa_rtsp_client *c, const char* key, const char* value) +{ +    pa_assert(c); +    pa_assert(key); +    pa_assert(value); + +    pa_headerlist_puts(c->headers, key, value); +} + +void pa_rtsp_remove_header(pa_rtsp_client *c, const char* key) +{ +    pa_assert(c); +    pa_assert(key); + +    pa_headerlist_remove(c->headers, key); +} + +static int rtsp_exec(pa_rtsp_client* c, const char* cmd, +                        const char* content_type, const char* content, +                        int expect_response, +                        pa_headerlist* headers) { +    pa_strbuf* buf; +    char* hdrs; +    ssize_t l; + +    pa_assert(c); +    pa_assert(c->url); + +    if (!cmd) +        return -1; + +    pa_log_debug("Sending command: %s", cmd); + +    buf = pa_strbuf_new(); +    pa_strbuf_printf(buf, "%s %s RTSP/1.0\r\nCSeq: %d\r\n", cmd, c->url, ++c->cseq); +    if (c->session) +        pa_strbuf_printf(buf, "Session: %s\r\n", c->session); + +    /* Add the headers */ +    if (headers) { +        hdrs = pa_headerlist_to_string(headers); +        pa_strbuf_puts(buf, hdrs); +        pa_xfree(hdrs); +    } + +    if (content_type && content) { +        pa_strbuf_printf(buf, "Content-Type: %s\r\nContent-Length: %d\r\n", +          content_type, (int)strlen(content)); +    } + +    pa_strbuf_printf(buf, "User-Agent: %s\r\n", c->useragent); + +    if (c->headers) { +        hdrs = pa_headerlist_to_string(c->headers); +        pa_strbuf_puts(buf, hdrs); +        pa_xfree(hdrs); +    } + +    pa_strbuf_puts(buf, "\r\n"); + +    if (content_type && content) { +        pa_strbuf_puts(buf, content); +    } + +    /* Our packet is created... now we can send it :) */ +    hdrs = pa_strbuf_tostring_free(buf); +    /*pa_log_debug("Submitting request:"); +    pa_log_debug(hdrs);*/ +    l = pa_iochannel_write(c->io, hdrs, strlen(hdrs)); +    pa_xfree(hdrs); + +    return 0; +} + + +int pa_rtsp_announce(pa_rtsp_client *c, const char* sdp) { +    pa_assert(c); +    if (!sdp) +        return -1; + +    c->state = STATE_ANNOUNCE; +    return rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL); +} + + +int pa_rtsp_setup(pa_rtsp_client* c) { +    pa_headerlist* headers; +    int rv; + +    pa_assert(c); + +    headers = pa_headerlist_new(); +    pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record"); + +    c->state = STATE_SETUP; +    rv = rtsp_exec(c, "SETUP", NULL, NULL, 1, headers); +    pa_headerlist_free(headers); +    return rv; +} + + +int pa_rtsp_record(pa_rtsp_client* c, uint16_t* seq, uint32_t* rtptime) { +    pa_headerlist* headers; +    int rv; +    char *info; + +    pa_assert(c); +    if (!c->session) { +        /* No seesion in progres */ +        return -1; +    } + +    /* Todo: Generate these values randomly as per spec */ +    *seq = *rtptime = 0; + +    headers = pa_headerlist_new(); +    pa_headerlist_puts(headers, "Range", "npt=0-"); +    info = pa_sprintf_malloc("seq=%u;rtptime=%u", *seq, *rtptime); +    pa_headerlist_puts(headers, "RTP-Info", info); +    pa_xfree(info); + +    c->state = STATE_RECORD; +    rv = rtsp_exec(c, "RECORD", NULL, NULL, 1, headers); +    pa_headerlist_free(headers); +    return rv; +} + + +int pa_rtsp_teardown(pa_rtsp_client *c) { +    pa_assert(c); + +    c->state = STATE_TEARDOWN; +    return rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL); +} + + +int pa_rtsp_setparameter(pa_rtsp_client *c, const char* param) { +    pa_assert(c); +    if (!param) +        return -1; + +    c->state = STATE_SET_PARAMETER; +    return rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL); +} + + +int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime) { +    pa_headerlist* headers; +    int rv; +    char *info; + +    pa_assert(c); + +    headers = pa_headerlist_new(); +    info = pa_sprintf_malloc("seq=%u;rtptime=%u", seq, rtptime); +    pa_headerlist_puts(headers, "RTP-Info", info); +    pa_xfree(info); + +    c->state = STATE_FLUSH; +    rv = rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers); +    pa_headerlist_free(headers); +    return rv; +} diff --git a/src/modules/rtp/rtsp_client.h b/src/modules/rtp/rtsp_client.h new file mode 100644 index 00000000..88fb3839 --- /dev/null +++ b/src/modules/rtp/rtsp_client.h @@ -0,0 +1,73 @@ +#ifndef foortspclienthfoo +#define foortspclienthfoo + +/*** +  This file is part of PulseAudio. + +  Copyright 2008 Colin Guthrie + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#include <inttypes.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <netdb.h> + +#include <pulsecore/memblockq.h> +#include <pulsecore/memchunk.h> +#include <pulsecore/socket-client.h> +#include <pulse/mainloop-api.h> + +#include "headerlist.h" + +typedef struct pa_rtsp_client pa_rtsp_client; +typedef enum { +  STATE_CONNECT, +  STATE_ANNOUNCE, +  STATE_SETUP, +  STATE_RECORD, +  STATE_FLUSH, +  STATE_TEARDOWN, +  STATE_SET_PARAMETER, +  STATE_DISCONNECTED +} pa_rtsp_state; +typedef void (*pa_rtsp_cb_t)(pa_rtsp_client *c, pa_rtsp_state state, pa_headerlist* hl, void *userdata); + +pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char* hostname, uint16_t port, const char* useragent); +void pa_rtsp_client_free(pa_rtsp_client* c); + +int pa_rtsp_connect(pa_rtsp_client* c); +void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userdata); + +void pa_rtsp_disconnect(pa_rtsp_client* c); + +const char* pa_rtsp_localip(pa_rtsp_client* c); +uint32_t pa_rtsp_serverport(pa_rtsp_client* c); +void pa_rtsp_set_url(pa_rtsp_client* c, const char* url); +void pa_rtsp_add_header(pa_rtsp_client *c, const char* key, const char* value); +void pa_rtsp_remove_header(pa_rtsp_client *c, const char* key); + +int pa_rtsp_announce(pa_rtsp_client* c, const char* sdp); + +int pa_rtsp_setup(pa_rtsp_client* c); +int pa_rtsp_record(pa_rtsp_client* c, uint16_t* seq, uint32_t* rtptime); +int pa_rtsp_teardown(pa_rtsp_client* c); + +int pa_rtsp_setparameter(pa_rtsp_client* c, const char* param); +int pa_rtsp_flush(pa_rtsp_client* c, uint16_t seq, uint32_t rtptime); + +#endif diff --git a/src/modules/rtp/sap.c b/src/modules/rtp/sap.c index 5d9b58fa..b0c95aa5 100644 --- a/src/modules/rtp/sap.c +++ b/src/modules/rtp/sap.c @@ -76,7 +76,7 @@ int pa_sap_send(pa_sap_context *c, pa_bool_t goodbye) {      socklen_t salen = sizeof(sa_buf);      struct iovec iov[4];      struct msghdr m; -    int k; +    ssize_t k;      if (getsockname(c->fd, sa, &salen) < 0) {          pa_log("getsockname() failed: %s\n", pa_cstrerror(errno)); @@ -94,7 +94,7 @@ int pa_sap_send(pa_sap_context *c, pa_bool_t goodbye) {      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 ? 4 : 16; +    iov[1].iov_len = sa->sa_family == AF_INET ? 4U : 16U;      iov[2].iov_base = (char*) MIME_TYPE;      iov[2].iov_len = sizeof(MIME_TYPE); @@ -113,7 +113,7 @@ int pa_sap_send(pa_sap_context *c, pa_bool_t goodbye) {      if ((k = sendmsg(c->fd, &m, MSG_DONTWAIT)) < 0)          pa_log_warn("sendmsg() failed: %s\n", pa_cstrerror(errno)); -    return k; +    return (int) k;  }  pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd) { @@ -128,10 +128,10 @@ pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd) {  int pa_sap_recv(pa_sap_context *c, pa_bool_t *goodbye) {      struct msghdr m;      struct iovec iov; -    int size, k; +    int size;      char *buf = NULL, *e;      uint32_t header; -    int six, ac; +    unsigned six, ac, k;      ssize_t r;      pa_assert(c); @@ -142,11 +142,11 @@ int pa_sap_recv(pa_sap_context *c, pa_bool_t *goodbye) {          goto fail;      } -    buf = pa_xnew(char, size+1); +    buf = pa_xnew(char, (unsigned) size+1);      buf[size] = 0;      iov.iov_base = buf; -    iov.iov_len = size; +    iov.iov_len = (size_t) size;      m.msg_name = NULL;      m.msg_namelen = 0; @@ -184,21 +184,21 @@ int pa_sap_recv(pa_sap_context *c, pa_bool_t *goodbye) {          goto fail;      } -    six = (header >> 28) & 1; -    ac = (header >> 16) & 0xFF; +    six = (header >> 28) & 1U; +    ac = (header >> 16) & 0xFFU; -    k = 4 + (six ? 16 : 4) + ac*4; -    if (size < k) { +    k = 4 + (six ? 16U : 4U) + ac*4U; +    if ((unsigned) size < k) {          pa_log_warn("SAP packet too short (AD).");          goto fail;      }      e = buf + k; -    size -= k; +    size -= (int) k;      if ((unsigned) size >= sizeof(MIME_TYPE) && !strcmp(e, MIME_TYPE)) {          e += sizeof(MIME_TYPE); -        size -= sizeof(MIME_TYPE); +        size -= (int) sizeof(MIME_TYPE);      } else if ((unsigned) size < sizeof(PA_SDP_HEADER)-1 || strncmp(e, PA_SDP_HEADER, sizeof(PA_SDP_HEADER)-1)) {          pa_log_warn("Invalid SDP header.");          goto fail; @@ -207,7 +207,7 @@ int pa_sap_recv(pa_sap_context *c, pa_bool_t *goodbye) {      if (c->sdp_data)          pa_xfree(c->sdp_data); -    c->sdp_data = pa_xstrndup(e, size); +    c->sdp_data = pa_xstrndup(e, (unsigned) size);      pa_xfree(buf);      *goodbye = !!((header >> 26) & 1); diff --git a/src/modules/rtp/sdp.c b/src/modules/rtp/sdp.c index cef90433..59989e1a 100644 --- a/src/modules/rtp/sdp.c +++ b/src/modules/rtp/sdp.c @@ -55,7 +55,7 @@ char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, u      if (!(u = pa_get_user_name(un, sizeof(un))))          u = "-"; -    ntp = time(NULL) + 2208988800U; +    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))); @@ -99,10 +99,10 @@ static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) {          return NULL;      if (sscanf(c, "%u/%u", &rate, &channels) == 2) { -        ss->rate = rate; -        ss->channels = channels; +        ss->rate = (uint32_t) rate; +        ss->channels = (uint8_t) channels;      } else if (sscanf(c, "%u", &rate) == 2) { -        ss->rate = rate; +        ss->rate = (uint32_t) rate;          ss->channels = 1;      } else          return NULL; diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index db0c4b3f..fd313bd3 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -198,10 +198,11 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p      pa_assert(m);      pa_assert(channels > 0);      pa_assert(channels <= PA_CHANNELS_MAX); +    pa_assert(def < PA_CHANNEL_MAP_DEF_MAX);      pa_channel_map_init(m); -    m->channels = channels; +    m->channels = (uint8_t) channels;      switch (def) {          case PA_CHANNEL_MAP_AIFF: @@ -287,9 +288,6 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p          case PA_CHANNEL_MAP_AUX: {              unsigned i; -            if (channels >= PA_CHANNELS_MAX) -                return NULL; -              for (i = 0; i < channels; i++)                  m->map[i] = PA_CHANNEL_POSITION_AUX0 + i; @@ -391,7 +389,7 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p          default: -            return NULL; +            pa_assert_not_reached();      }  } @@ -401,6 +399,7 @@ pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels,      pa_assert(m);      pa_assert(channels > 0);      pa_assert(channels <= PA_CHANNELS_MAX); +    pa_assert(def < PA_CHANNEL_MAP_DEF_MAX);      pa_channel_map_init(m); @@ -415,7 +414,7 @@ pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels,                  i++;              } -            m->channels = channels; +            m->channels = (uint8_t) channels;              return m;          } @@ -460,13 +459,20 @@ int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {  char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {      unsigned channel; -    int first = 1; +    pa_bool_t first = TRUE;      char *e;      pa_assert(s);      pa_assert(l > 0);      pa_assert(map); +    pa_init_i18n(); + +    if (!pa_channel_map_valid(map)) { +        pa_snprintf(s, l, _("(invalid)")); +        return s; +    } +      *(e = s) = 0;      for (channel = 0; channel < map->channels && l > 1; channel++) { @@ -475,7 +481,7 @@ char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {                        pa_channel_position_to_string(map->map[channel]));          e = strchr(e, 0); -        first = 0; +        first = FALSE;      }      return s; @@ -489,7 +495,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {      pa_assert(rmap);      pa_assert(s); -    memset(&map, 0, sizeof(map)); +    pa_channel_map_init(&map);      if (strcmp(s, "stereo") == 0) {          map.channels = 2; @@ -552,11 +558,22 @@ int pa_channel_map_valid(const pa_channel_map *map) {      if (map->channels <= 0 || map->channels > PA_CHANNELS_MAX)          return 0; -    for (c = 0; c < map->channels; c++) { - -        if (map->map[c] < 0 ||map->map[c] >= PA_CHANNEL_POSITION_MAX) +    for (c = 0; c < map->channels; c++) +        if (map->map[c] < 0 || map->map[c] >= PA_CHANNEL_POSITION_MAX)              return 0; -    }      return 1;  } + +int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *ss) { +    pa_assert(map); +    pa_assert(ss); + +    if (!pa_channel_map_valid(map)) +        return 0; + +    if (!pa_sample_spec_valid(ss)) +        return 0; + +    return map->channels == ss->channels; +} diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h index 7c32b868..d7d19d79 100644 --- a/src/pulse/channelmap.h +++ b/src/pulse/channelmap.h @@ -142,24 +142,42 @@ typedef enum pa_channel_position {  /** A list of channel mapping definitions for pa_channel_map_init_auto() */  typedef enum pa_channel_map_def { -    PA_CHANNEL_MAP_AIFF,   /**< The mapping from RFC3551, which is based on AIFF-C */ -    PA_CHANNEL_MAP_ALSA,   /**< The default mapping used by ALSA */ -    PA_CHANNEL_MAP_AUX,    /**< Only aux channels */ -    PA_CHANNEL_MAP_WAVEEX, /**< Microsoft's WAVEFORMATEXTENSIBLE mapping */ -    PA_CHANNEL_MAP_OSS,    /**< The default channel mapping used by OSS as defined in the OSS 4.0 API specs */ +    PA_CHANNEL_MAP_AIFF, +    /**< The mapping from RFC3551, which is based on AIFF-C */ -    PA_CHANNEL_MAP_DEFAULT = PA_CHANNEL_MAP_AIFF /**< The default channel map */ +    PA_CHANNEL_MAP_ALSA, +    /**< The default mapping used by ALSA */ + +    PA_CHANNEL_MAP_AUX, +    /**< Only aux channels */ + +    PA_CHANNEL_MAP_WAVEEX, +    /**< Microsoft's WAVEFORMATEXTENSIBLE mapping */ + +    PA_CHANNEL_MAP_OSS, +    /**< The default channel mapping used by OSS as defined in the OSS 4.0 API specs */ + +    /**< Upper limit of valid channel mapping definitions */ +    PA_CHANNEL_MAP_DEF_MAX, + +    PA_CHANNEL_MAP_DEFAULT = PA_CHANNEL_MAP_AIFF +    /**< The default channel map */  } pa_channel_map_def_t;  /** A channel map which can be used to attach labels to specific   * channels of a stream. These values are relevant for conversion and   * mixing of streams */  typedef struct pa_channel_map { -    uint8_t channels; /**< Number of channels */ -    pa_channel_position_t map[PA_CHANNELS_MAX]; /**< Channel labels */ +    uint8_t channels; +    /**< Number of channels */ + +    pa_channel_position_t map[PA_CHANNELS_MAX]; +    /**< Channel labels */  } pa_channel_map; -/** Initialize the specified channel map and return a pointer to it */ +/** Initialize the specified channel map and return a pointer to + * it. The channel map will have a defined state but + * pa_channel_map_valid() will fail for it. */  pa_channel_map* pa_channel_map_init(pa_channel_map *m);  /** Initialize the specified channel map for monoaural audio and return a pointer to it */ @@ -186,7 +204,11 @@ const char* pa_channel_position_to_string(pa_channel_position_t pos) PA_GCC_PURE  /** Return a human readable text label for the specified channel position. \since 0.9.7 */  const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos); -/** The maximum length of strings returned by pa_channel_map_snprint() */ +/** The maximum length of strings returned by + * pa_channel_map_snprint(). Please note that this value can change + * with any release without warning and without being considered API + * or ABI breakage. You should not use this definition anywhere where + * it might become part of an ABI. */  #define PA_CHANNEL_MAP_SNPRINT_MAX 336  /** Make a humand readable string from the specified channel map */ @@ -198,9 +220,13 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *map, const char *s);  /** Compare two channel maps. Return 1 if both match. */  int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) PA_GCC_PURE; -/** Return non-zero of the specified channel map is considered valid */ +/** Return non-zero if the specified channel map is considered valid */  int pa_channel_map_valid(const pa_channel_map *map) PA_GCC_PURE; +/** Return non-zero if the specified channel map is compatible with + * the specified sample spec. \since 0.9.12 */ +int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *ss) PA_GCC_PURE; +  PA_C_DECL_END  #endif diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index 739ef161..58d64642 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -61,6 +61,7 @@ static const pa_client_conf default_conf = {      .disable_shm = FALSE,      .cookie_file = NULL,      .cookie_valid = FALSE, +    .shm_size = 0  };  pa_client_conf *pa_client_conf_new(void) { @@ -99,6 +100,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {          { "autospawn",              pa_config_parse_bool,    NULL },          { "cookie-file",            pa_config_parse_string,  NULL },          { "disable-shm",            pa_config_parse_bool,    NULL }, +        { "shm-size-bytes",         pa_config_parse_size,    NULL },          { NULL,                     NULL,                    NULL },      }; @@ -110,6 +112,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {      table[5].data = &c->autospawn;      table[6].data = &c->cookie_file;      table[7].data = &c->disable_shm; +    table[8].data = &c->shm_size;      if (filename) { diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h index 699279aa..4eac467e 100644 --- a/src/pulse/client-conf.h +++ b/src/pulse/client-conf.h @@ -31,6 +31,7 @@ typedef struct pa_client_conf {      pa_bool_t autospawn, disable_shm;      uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];      pa_bool_t cookie_valid; /* non-zero, when cookie is valid */ +    size_t shm_size;  } pa_client_conf;  /* Create a new configuration data object and reset it to defaults */ diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in index 8339d651..579bcc20 100644 --- a/src/pulse/client.conf.in +++ b/src/pulse/client.conf.in @@ -30,3 +30,4 @@  ; cookie-file =  ; disable-shm = no +; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB diff --git a/src/pulse/context.c b/src/pulse/context.c index 5be4078b..3145d9c8 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -81,8 +81,6 @@  #include "context.h" -#define AUTOSPAWN_LOCK "autospawn.lock" -  void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);  static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { @@ -99,23 +97,6 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {      [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event,      [PA_COMMAND_EXTENSION] = pa_command_extension  }; - -static void unlock_autospawn_lock_file(pa_context *c) { -    pa_assert(c); - -    if (c->autospawn_lock_fd >= 0) { -        char *lf; - -        if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) -            pa_log_warn(_("Cannot unlock autospawn because runtime path is no more.")); - -        pa_unlock_lockfile(lf, c->autospawn_lock_fd); -        pa_xfree(lf); - -        c->autospawn_lock_fd = -1; -    } -} -  static void context_free(pa_context *c);  pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { @@ -174,11 +155,12 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *      c->is_local = FALSE;      c->server_list = NULL;      c->server = NULL; -    c->autospawn_lock_fd = -1; -    memset(&c->spawn_api, 0, sizeof(c->spawn_api)); -    c->do_autospawn = FALSE; +      c->do_shm = FALSE; +    c->do_autospawn = FALSE; +    memset(&c->spawn_api, 0, sizeof(c->spawn_api)); +  #ifndef MSG_NOSIGNAL  #ifdef SIGPIPE      pa_check_signal_is_blocked(SIGPIPE); @@ -192,10 +174,10 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *      pa_client_conf_load(c->conf, NULL);      pa_client_conf_env(c->conf); -    if (!(c->mempool = pa_mempool_new(!c->conf->disable_shm))) { +    if (!(c->mempool = pa_mempool_new(!c->conf->disable_shm, c->conf->shm_size))) {          if (!c->conf->disable_shm) -            c->mempool = pa_mempool_new(0); +            c->mempool = pa_mempool_new(FALSE, c->conf->shm_size);          if (!c->mempool) {              context_free(c); @@ -246,8 +228,6 @@ static void context_free(pa_context *c) {      context_unlink(c); -    unlock_autospawn_lock_file(c); -      if (c->record_streams)          pa_dynarray_free(c->record_streams, NULL, NULL);      if (c->playback_streams) @@ -407,11 +387,11 @@ int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa          err = PA_ERR_UNKNOWN;      if (fail) { -        pa_context_fail(c, err); +        pa_context_fail(c, (int) err);          return -1;      } -    pa_context_set_error(c, err); +    pa_context_set_error(c, (int) err);      return 0;  } @@ -433,7 +413,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t      switch(c->state) {          case PA_CONTEXT_AUTHORIZING: {              pa_tagstruct *reply; -            pa_bool_t shm_on_remote; +            pa_bool_t shm_on_remote = FALSE;              if (pa_tagstruct_getu32(t, &c->version) < 0 ||                  !pa_tagstruct_eof(t)) { @@ -571,31 +551,89 @@ static void setup_context(pa_context *c, pa_iochannel *io) {      pa_context_unref(c);  } -static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata); +static char *get_old_legacy_runtime_dir(void) { +    char *p, u[128]; +    struct stat st; -#ifndef OS_IS_WIN32 +    if (!pa_get_user_name(u, sizeof(u))) +        return NULL; -static int context_connect_spawn(pa_context *c) { -    pid_t pid; -    int status, r; -    int fds[2] = { -1, -1} ; -    pa_iochannel *io; +    p = pa_sprintf_malloc("/tmp/pulse-%s", u); -    if (getuid() == 0) -        return -1; +    if (stat(p, &st) < 0) { +        pa_xfree(p); +        return NULL; +    } -    pa_context_ref(c); +    if (st.st_uid != getuid()) { +        pa_xfree(p); +        return NULL; +    } -    if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { -        pa_log_error(_("socketpair(): %s"), pa_cstrerror(errno)); -        pa_context_fail(c, PA_ERR_INTERNAL); -        goto fail; +    return p; +} + +static char *get_very_old_legacy_runtime_dir(void) { +    char *p, h[128]; +    struct stat st; + +    if (!pa_get_home_dir(h, sizeof(h))) +        return NULL; + +    p = pa_sprintf_malloc("%s/.pulse", h); + +    if (stat(p, &st) < 0) { +        pa_xfree(p); +        return NULL; +    } + +    if (st.st_uid != getuid()) { +        pa_xfree(p); +        return NULL;      } -    pa_make_fd_cloexec(fds[0]); +    return p; +} -    pa_make_socket_low_delay(fds[0]); -    pa_make_socket_low_delay(fds[1]); + +static pa_strlist *prepend_per_user(pa_strlist *l) { +    char *ufn; +    static char *legacy_dir; + +    /* The very old per-user instance path (< 0.9.11). This is supported only to ease upgrades */ +    if ((legacy_dir = get_very_old_legacy_runtime_dir())) { +        char *p = pa_sprintf_malloc("%s" PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET, legacy_dir); +        l = pa_strlist_prepend(l, p); +        pa_xfree(p); +        pa_xfree(legacy_dir); +    } + +    /* The old per-user instance path (< 0.9.12). This is supported only to ease upgrades */ +    if ((legacy_dir = get_old_legacy_runtime_dir())) { +        char *p = pa_sprintf_malloc("%s" PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET, legacy_dir); +        l = pa_strlist_prepend(l, p); +        pa_xfree(p); +        pa_xfree(legacy_dir); +    } + +    /* The per-user instance */ +    if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) { +        l = pa_strlist_prepend(l, ufn); +        pa_xfree(ufn); +    } + +    return l; +} + +#ifndef OS_IS_WIN32 + +static int context_autospawn(pa_context *c) { +    pid_t pid; +    int status, r; + +    pa_log_debug("Trying to autospawn..."); + +    pa_context_ref(c);      if (c->spawn_api.prefork)          c->spawn_api.prefork(); @@ -611,31 +649,22 @@ static int context_connect_spawn(pa_context *c) {      } else if (!pid) {          /* Child */ -        char t[128];          const char *state = NULL;  #define MAX_ARGS 64          const char * argv[MAX_ARGS+1];          int n; -        char *f; - -        pa_close_all(fds[1], -1); - -        f = pa_sprintf_malloc("%i", fds[1]); -        pa_set_env("PULSE_PASSED_FD", f); -        pa_xfree(f);          if (c->spawn_api.atfork)              c->spawn_api.atfork(); +        pa_close_all(-1); +          /* Setup argv */          n = 0;          argv[n++] = c->conf->daemon_binary; -        argv[n++] = "--daemonize=yes"; - -        pa_snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]); -        argv[n++] = strdup(t); +        argv[n++] = "--start";          while (n < MAX_ARGS) {              char *a; @@ -655,14 +684,13 @@ static int context_connect_spawn(pa_context *c) {      /* Parent */ -    pa_assert_se(pa_close(fds[1]) == 0); -    fds[1] = -1; - -    r = waitpid(pid, &status, 0); -      if (c->spawn_api.postfork)          c->spawn_api.postfork(); +    do { +        r = waitpid(pid, &status, 0); +    } while (r < 0 && errno == EINTR); +      if (r < 0) {          pa_log(_("waitpid(): %s"), pa_cstrerror(errno));          pa_context_fail(c, PA_ERR_INTERNAL); @@ -672,21 +700,11 @@ static int context_connect_spawn(pa_context *c) {          goto fail;      } -    c->is_local = TRUE; - -    unlock_autospawn_lock_file(c); - -    io = pa_iochannel_new(c->mainloop, fds[0], fds[0]); -    setup_context(c, io); -      pa_context_unref(c);      return 0;  fail: -    pa_close_pipe(fds); - -    unlock_autospawn_lock_file(c);      pa_context_unref(c); @@ -695,6 +713,8 @@ fail:  #endif /* OS_IS_WIN32 */ +static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata); +  static int try_next_connection(pa_context *c) {      char *u = NULL;      int r = -1; @@ -712,8 +732,18 @@ static int try_next_connection(pa_context *c) {  #ifndef OS_IS_WIN32              if (c->do_autospawn) { -                r = context_connect_spawn(c); -                goto finish; + +                if ((r = context_autospawn(c)) < 0) +                    goto finish; + +                /* Autospawn only once */ +                c->do_autospawn = FALSE; + +                /* Connect only to per-user sockets this time */ +                c->server_list = prepend_per_user(c->server_list); + +                /* Retry connection */ +                continue;              }  #endif @@ -768,36 +798,12 @@ static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userd          goto finish;      } -    unlock_autospawn_lock_file(c);      setup_context(c, io);  finish:      pa_context_unref(c);  } - -static char *get_legacy_runtime_dir(void) { -    char *p, u[128]; -    struct stat st; - -    if (!pa_get_user_name(u, sizeof(u))) -        return NULL; - -    p = pa_sprintf_malloc("/tmp/pulse-%s", u); - -    if (stat(p, &st) < 0) { -        pa_xfree(p); -        return NULL; -    } - -    if (st.st_uid != getuid()) { -        pa_xfree(p); -        return NULL; -    } - -    return p; -} -  int pa_context_connect(          pa_context *c,          const char *server, @@ -825,12 +831,13 @@ int pa_context_connect(              pa_context_fail(c, PA_ERR_INVALIDSERVER);              goto finish;          } +      } else { -        char *d, *ufn; -        static char *legacy_dir; +        char *d;          /* Prepend in reverse order */ +        /* Follow the X display */          if ((d = getenv("DISPLAY"))) {              char *e;              d = pa_xstrdup(d); @@ -843,43 +850,27 @@ int pa_context_connect(              pa_xfree(d);          } -        c->server_list = pa_strlist_prepend(c->server_list, "tcp6:localhost"); -        c->server_list = pa_strlist_prepend(c->server_list, "tcp4:localhost"); +        /* Add TCP/IP on the localhost */ +        c->server_list = pa_strlist_prepend(c->server_list, "tcp6:[::1]"); +        c->server_list = pa_strlist_prepend(c->server_list, "tcp4:127.0.0.1"); -        /* The system wide instance */ +        /* The system wide instance via PF_LOCAL */          c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET); -        /* The old per-user instance path. This is supported only to ease upgrades */ -        if ((legacy_dir = get_legacy_runtime_dir())) { -            char *p = pa_sprintf_malloc("%s" PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET, legacy_dir); -            c->server_list = pa_strlist_prepend(c->server_list, p); -            pa_xfree(p); -            pa_xfree(legacy_dir); -        } +        /* The user instance via PF_LOCAL */ +        c->server_list = prepend_per_user(c->server_list); -        /* The per-user instance */ -        if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) { -            c->server_list = pa_strlist_prepend(c->server_list, ufn); -            pa_xfree(ufn); -        } - -        /* Wrap the connection attempts in a single transaction for sane autospawn locking */ +        /* Set up autospawning */          if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) { -            char *lf; -            if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) { -                pa_context_fail(c, PA_ERR_ACCESS); -                goto finish; -            } +            if (getuid() == 0) +                pa_log_debug("Not doing autospawn since we are root."); +            else { +                c->do_autospawn = TRUE; -            pa_assert(c->autospawn_lock_fd <= 0); -            c->autospawn_lock_fd = pa_lock_lockfile(lf); -            pa_xfree(lf); - -            if (api) -                c->spawn_api = *api; - -            c->do_autospawn = TRUE; +                if (api) +                    c->spawn_api = *api; +            }          }      } @@ -938,11 +929,11 @@ int pa_context_is_pending(pa_context *c) {  static void set_dispatch_callbacks(pa_operation *o); -static void pdispatch_drain_callback(PA_GCC_UNUSED pa_pdispatch*pd, void *userdata) { +static void pdispatch_drain_callback(pa_pdispatch*pd, void *userdata) {      set_dispatch_callbacks(userdata);  } -static void pstream_drain_callback(PA_GCC_UNUSED pa_pstream *s, void *userdata) { +static void pstream_drain_callback(pa_pstream *s, void *userdata) {      set_dispatch_callbacks(userdata);  } @@ -994,7 +985,7 @@ pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *u      return o;  } -void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      int success = 1; @@ -1146,7 +1137,7 @@ const char* pa_context_get_server(pa_context *c) {      return c->server;  } -uint32_t pa_context_get_protocol_version(PA_GCC_UNUSED pa_context *c) { +uint32_t pa_context_get_protocol_version(pa_context *c) {      return PA_PROTOCOL_VERSION;  } diff --git a/src/pulse/def.h b/src/pulse/def.h index aa07d1c9..ace56574 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -57,7 +57,7 @@ static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {  /** The state of a stream */  typedef enum pa_stream_state { -    PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */ +    PA_STREAM_UNCONNECTED,  /**< The stream is not yet connected to any sink or source */      PA_STREAM_CREATING,     /**< The stream is being created */      PA_STREAM_READY,        /**< The stream is established, you may pass audio data to it now */      PA_STREAM_FAILED,       /**< An error occured that made the stream invalid */ @@ -83,9 +83,15 @@ typedef enum pa_operation_state {  /** Some special flags for contexts. */  typedef enum pa_context_flags { -    PA_CONTEXT_NOAUTOSPAWN = 1 /**< Disabled autospawning of the PulseAudio daemon if required */ +    PA_CONTEXT_NOAUTOSPAWN = 1 +    /**< Disabled autospawning of the PulseAudio daemon if required */  } pa_context_flags_t; +/** \cond fulldocs */ +/* Allow clients to check with #ifdef for those flags */ +#define PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN +/** \endcond */ +  /** The direction of a pa_stream object */  typedef enum pa_stream_direction {      PA_STREAM_NODIRECTION,   /**< Invalid direction */ @@ -96,237 +102,226 @@ typedef enum pa_stream_direction {  /** Some special flags for stream connections. */  typedef enum pa_stream_flags { -    PA_STREAM_START_CORKED = 1,       /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */ -    PA_STREAM_INTERPOLATE_TIMING = 2, /**< Interpolate the latency for -                                       * this stream. When enabled, -                                       * pa_stream_get_latency() and -                                       * pa_stream_get_time() will try -                                       * to estimate the current -                                       * record/playback time based on -                                       * the local time that passed -                                       * since the last timing info -                                       * update.  Using this option -                                       * has the advantage of not -                                       * requiring a whole roundtrip -                                       * when the current -                                       * playback/recording time is -                                       * needed. Consider using this -                                       * option when requesting -                                       * latency information -                                       * frequently. This is -                                       * especially useful on long -                                       * latency network -                                       * connections. It makes a lot -                                       * of sense to combine this -                                       * option with -                                       * PA_STREAM_AUTO_TIMING_UPDATE. */ -    PA_STREAM_NOT_MONOTONIC = 4,    /**< Don't force the time to -                                      * increase monotonically. If -                                      * this option is enabled, -                                      * pa_stream_get_time() will not -                                      * necessarily return always -                                      * monotonically increasing time -                                      * values on each call. This may -                                      * confuse applications which -                                      * cannot deal with time going -                                      * 'backwards', but has the -                                      * advantage that bad transport -                                      * latency estimations that -                                      * caused the time to to jump -                                      * ahead can be corrected -                                      * quickly, without the need to -                                      * wait. (Please note that this -                                      * flag was named -                                      * PA_STREAM_NOT_MONOTONOUS in -                                      * releases prior to 0.9.11. The -                                      * old name is still defined too, -                                      * for compatibility reasons. */ -    PA_STREAM_AUTO_TIMING_UPDATE = 8, /**< If set timing update requests -                                       * are issued periodically -                                       * automatically. Combined with -                                       * PA_STREAM_INTERPOLATE_TIMING -                                       * you will be able to query the -                                       * current time and latency with -                                       * pa_stream_get_time() and -                                       * pa_stream_get_latency() at -                                       * all times without a packet -                                       * round trip.*/ -    PA_STREAM_NO_REMAP_CHANNELS = 16, /**< Don't remap channels by -                                       * their name, instead map them -                                       * simply by their -                                       * index. Implies -                                       * PA_STREAM_NO_REMIX_CHANNELS. Only -                                       * supported when the server is -                                       * at least PA 0.9.8. It is -                                       * ignored on older -                                       * servers.\since 0.9.8 */ -    PA_STREAM_NO_REMIX_CHANNELS = 32, /**< When remapping channels by -                                       * name, don't upmix or downmix -                                       * them to related -                                       * channels. Copy them into -                                       * matching channels of the -                                       * device 1:1. Only supported -                                       * when the server is at least -                                       * PA 0.9.8. It is ignored on -                                       * older servers. \since -                                       * 0.9.8 */ -    PA_STREAM_FIX_FORMAT = 64, /**< Use the sample format of the -                                * sink/device this stream is being -                                * connected to, and possibly ignore -                                * the format the sample spec contains -                                * -- but you still have to pass a -                                * valid value in it as a hint to -                                * PulseAudio what would suit your -                                * stream best. If this is used you -                                * should query the used sample format -                                * after creating the stream by using -                                * pa_stream_get_sample_spec(). Also, -                                * if you specified manual buffer -                                * metrics it is recommended to update -                                * them with -                                * pa_stream_set_buffer_attr() to -                                * compensate for the changed frame -                                * sizes. Only supported when the -                                * server is at least PA 0.9.8. It is -                                * ignored on older servers. \since -                                * 0.9.8 */ - -    PA_STREAM_FIX_RATE = 128, /**< Use the sample rate of the sink, -                               * and possibly ignore the rate the -                               * sample spec contains. Usage similar -                               * to PA_STREAM_FIX_FORMAT.Only -                               * supported when the server is at least -                               * PA 0.9.8. It is ignored on older -                               * servers. \since 0.9.8 */ - -    PA_STREAM_FIX_CHANNELS = 256, /**< Use the number of channels and -                               * the channel map of the sink, and -                               * possibly ignore the number of -                               * channels and the map the sample spec -                               * and the passed channel map -                               * contains. Usage similar to -                               * PA_STREAM_FIX_FORMAT. Only supported -                               * when the server is at least PA -                               * 0.9.8. It is ignored on older -                               * servers. \since 0.9.8 */ -    PA_STREAM_DONT_MOVE = 512, /**< Don't allow moving of this stream to -                              * another sink/device. Useful if you use -                              * any of the PA_STREAM_FIX_ flags and -                              * want to make sure that resampling -                              * never takes place -- which might -                              * happen if the stream is moved to -                              * another sink/source whith a different -                              * sample spec/channel map. Only -                              * supported when the server is at least -                              * PA 0.9.8. It is ignored on older -                              * servers. \since 0.9.8 */ -    PA_STREAM_VARIABLE_RATE = 1024, /**< Allow dynamic changing of the -                                     * sampling rate during playback -                                     * with -                                     * pa_stream_update_sample_rate(). Only -                                     * supported when the server is at -                                     * least PA 0.9.8. It is ignored -                                     * on older servers. \since -                                     * 0.9.8 */ -    PA_STREAM_PEAK_DETECT = 2048, /**< Find peaks instead of -                                   * resampling. \since 0.9.11 */ - -    PA_STREAM_START_MUTED = 4096,  /**< Create in muted state. \since 0.9.11 */ - -    PA_STREAM_ADJUST_LATENCY = 8192, /**< Try to adjust the latency of -                                      * the sink/source based on the -                                      * requested buffer metrics and -                                      * adjust buffer metrics -                                      * accordingly. See pa_buffer_attr \since 0.9.11 */ + +    PA_STREAM_START_CORKED = 0x0001U, +    /**< Create the stream corked, requiring an explicit +     * pa_stream_cork() call to uncork it. */ + +    PA_STREAM_INTERPOLATE_TIMING = 0x0002U, +    /**< Interpolate the latency for this stream. When enabled, +     * pa_stream_get_latency() and pa_stream_get_time() will try to +     * estimate the current record/playback time based on the local +     * time that passed since the last timing info update.  Using this +     * option has the advantage of not requiring a whole roundtrip +     * when the current playback/recording time is needed. Consider +     * using this option when requesting latency information +     * frequently. This is especially useful on long latency network +     * connections. It makes a lot of sense to combine this option +     * with PA_STREAM_AUTO_TIMING_UPDATE. */ + +    PA_STREAM_NOT_MONOTONIC = 0x0004U, +    /**< Don't force the time to increase monotonically. If this +     * option is enabled, pa_stream_get_time() will not necessarily +     * return always monotonically increasing time values on each +     * call. This may confuse applications which cannot deal with time +     * going 'backwards', but has the advantage that bad transport +     * latency estimations that caused the time to to jump ahead can +     * be corrected quickly, without the need to wait. (Please note +     * that this flag was named PA_STREAM_NOT_MONOTONOUS in releases +     * prior to 0.9.11. The old name is still defined too, for +     * compatibility reasons. */ + +    PA_STREAM_AUTO_TIMING_UPDATE = 0x0008U, +    /**< If set timing update requests are issued periodically +     * automatically. Combined with PA_STREAM_INTERPOLATE_TIMING you +     * will be able to query the current time and latency with +     * pa_stream_get_time() and pa_stream_get_latency() at all times +     * without a packet round trip.*/ + +    PA_STREAM_NO_REMAP_CHANNELS = 0x0010U, +    /**< Don't remap channels by their name, instead map them simply +     * by their index. Implies PA_STREAM_NO_REMIX_CHANNELS. Only +     * supported when the server is at least PA 0.9.8. It is ignored +     * on older servers.\since 0.9.8 */ + +    PA_STREAM_NO_REMIX_CHANNELS = 0x0020U, +    /**< When remapping channels by name, don't upmix or downmix them +     * to related channels. Copy them into matching channels of the +     * device 1:1. Only supported when the server is at least PA +     * 0.9.8. It is ignored on older servers. \since 0.9.8 */ + +    PA_STREAM_FIX_FORMAT = 0x0040U, +    /**< Use the sample format of the sink/device this stream is being +     * connected to, and possibly ignore the format the sample spec +     * contains -- but you still have to pass a valid value in it as a +     * hint to PulseAudio what would suit your stream best. If this is +     * used you should query the used sample format after creating the +     * stream by using pa_stream_get_sample_spec(). Also, if you +     * specified manual buffer metrics it is recommended to update +     * them with pa_stream_set_buffer_attr() to compensate for the +     * changed frame sizes. Only supported when the server is at least +     * PA 0.9.8. It is ignored on older servers. \since 0.9.8 */ + +    PA_STREAM_FIX_RATE = 0x0080U, +    /**< Use the sample rate of the sink, and possibly ignore the rate +     * the sample spec contains. Usage similar to +     * PA_STREAM_FIX_FORMAT.Only supported when the server is at least +     * PA 0.9.8. It is ignored on older servers. \since 0.9.8 */ + +    PA_STREAM_FIX_CHANNELS = 0x0100, +    /**< Use the number of channels and the channel map of the sink, +     * and possibly ignore the number of channels and the map the +     * sample spec and the passed channel map contains. Usage similar +     * to PA_STREAM_FIX_FORMAT. Only supported when the server is at +     * least PA 0.9.8. It is ignored on older servers. \since 0.9.8 */ + +    PA_STREAM_DONT_MOVE = 0x0200U, +    /**< Don't allow moving of this stream to another +     * sink/device. Useful if you use any of the PA_STREAM_FIX_ flags +     * and want to make sure that resampling never takes place -- +     * which might happen if the stream is moved to another +     * sink/source whith a different sample spec/channel map. Only +     * supported when the server is at least PA 0.9.8. It is ignored +     * on older servers. \since 0.9.8 */ + +    PA_STREAM_VARIABLE_RATE = 0x0400U, +    /**< Allow dynamic changing of the sampling rate during playback +     * with pa_stream_update_sample_rate(). Only supported when the +     * server is at least PA 0.9.8. It is ignored on older +     * servers. \since 0.9.8 */ + +    PA_STREAM_PEAK_DETECT = 0x0800U, +    /**< Find peaks instead of resampling. \since 0.9.11 */ + +    PA_STREAM_START_MUTED = 0x1000U, +    /**< Create in muted state. If neither PA_STREAM_START_UNMUTED nor +     * PA_STREAM_START_MUTED it is left to the server to decide +     * whether to create the stream in muted or in unmuted +     * state. \since 0.9.11 */ + +    PA_STREAM_ADJUST_LATENCY = 0x2000U, +    /**< Try to adjust the latency of the sink/source based on the +     * requested buffer metrics and adjust buffer metrics +     * accordingly. Also see pa_buffer_attr. This option may not be +     * specified at the same time as PA_STREAM_EARLY_REQUESTS. \since +     * 0.9.11 */ + +    PA_STREAM_EARLY_REQUESTS = 0x4000U, +    /**< Enable compatibility mode for legacy clients that rely on a +     * "classic" hardware device fragment-style playback model. If +     * this option is set, the minreq value of the buffer metrics gets +     * a new meaning: instead of just specifying that no requests +     * asking for less new data than this value will be made to the +     * client it will also guarantee that requests are generated as +     * early as this limit is reached. This flag should only be set in +     * very few situations where compatiblity with a fragment-based +     * playback model needs to be kept and the client applications +     * cannot deal with data requests that are delayed to the latest +     * moment possible. (Usually these are programs that use usleep() +     * or a similar call in their playback loops instead of sleeping +     * on the device itself.) Also see pa_buffer_attr. This option may +     * not be specified at the same time as +     * PA_STREAM_ADJUST_LATENCY. \since 0.9.12 */ + +    PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND = 0x8000U, +    /**< If set this stream won't be taken into account when we it is +     * checked whether the device this stream is connected to should +     * auto-suspend. \ since 0.9.14 */ + +    PA_STREAM_START_UNMUTED = 0x10000U +    /**< Create in unmuted state. If neither PA_STREAM_START_UNMUTED +     * nor PA_STREAM_START_MUTED it is left to the server to decide +     * whether to create the stream in muted or in unmuted +     * state. \since 0.9.14 */ +  } pa_stream_flags_t; +/** \cond fulldocs */ -/** English is an evil language */ +/* English is an evil language */  #define PA_STREAM_NOT_MONOTONOUS PA_STREAM_NOT_MONOTONIC +/* Allow clients to check with #ifdef for those flags */ +#define PA_STREAM_START_CORKED PA_STREAM_START_CORKED +#define PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING +#define PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC +#define PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE +#define PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS +#define PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS +#define PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT +#define PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE +#define PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS +#define PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE +#define PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE +#define PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT +#define PA_STREAM_START_MUTED PA_STREAM_START_MUTED +#define PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY +#define PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS +#define PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND +#define PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED + +/** \endcond */ +  /** Playback and record buffer metrics */  typedef struct pa_buffer_attr { -    uint32_t maxlength;      /**< Maximum length of the -                              * buffer. Setting this to (uint32_t) -1 will -                              * initialize this to the maximum value -                              * supported by server, which is -                              * recommended. */ -    uint32_t tlength;        /**< Playback only: target length of the -                              * buffer. The server tries to assure -                              * that at least tlength bytes are always -                              * available in the per-stream -                              * server-side playback buffer. It is -                              * recommended to set this to (uint32_t) -                              * -1, which will initialize this to a -                              * value that is deemed sensible by the -                              * server. However, this value will -                              * default to something like 2s, i.e. for -                              * applications that have specific -                              * latency requirements this value should -                              * be set to the maximum latency that the -                              * application can deal with. When -                              * PA_STREAM_ADJUST_LATENCY is not set -                              * this value will influence only the -                              * per-stream playback buffer size. When -                              * PA_STREAM_ADJUST_LATENCY is set the -                              * overall latency of the sink plus the -                              * playback buffer size is configured to -                              * this value. Set -                              * PA_STREAM_ADJUST_LATENCY if you are -                              * interested in adjusting the overall -                              * latency. Don't set it if you are -                              * interested in configuring the -                              * server-sider per-stream playback -                              * buffer size. */ -    uint32_t prebuf;         /**< Playback only: pre-buffering. The -                              * server does not start with playback -                              * before at least prebug bytes are -                              * available in the buffer. It is -                              * recommended to set this to (uint32_t) -                              * -1, which will initialize this to the -                              * same value as tlength, whatever that -                              * may be. Initialize to 0 to enable -                              * manual start/stop control of the -                              * stream. This means that playback will -                              * not stop on underrun and playback will -                              * not start automatically. Instead -                              * pa_stream_corked() needs to be called -                              * explicitly. If you set this value to 0 -                              * you should also set -                              * PA_STREAM_START_CORKED. */ -    uint32_t minreq;         /**< Playback only: minimum request. The -                              * server does not request less than -                              * minreq bytes from the client, instead -                              * waits until the buffer is free enough -                              * to request more bytes at once. It is -                              * recommended to set this to (uint32_t) -                              * -1, which will initialize this to a -                              * value that is deemed sensible by the -                              * server. This should be set to a value -                              * that gives PulseAudio enough time to -                              * move the data from the per-stream -                              * playback buffer into the hardware -                              * playback buffer. */ -    uint32_t fragsize;       /**< Recording only: fragment size. The -                              * server sends data in blocks of -                              * fragsize bytes size. Large values -                              * deminish interactivity with other -                              * operations on the connection context -                              * but decrease control overhead. It is -                              * recommended to set this to (uint32_t) -                              * -1, which will initialize this to a -                              * value that is deemed sensible by the -                              * server. However, this value will -                              * default to something like 2s, i.e. for -                              * applications that have specific -                              * latency requirements this value should -                              * be set to the maximum latency that the -                              * application can deal with. If -                              * PA_STREAM_ADJUST_LATENCY is set the -                              * overall source latency will be -                              * adjusted according to this value. If -                              * it is not set the source latency is -                              * left unmodified. */ +    uint32_t maxlength; +    /**< Maximum length of the buffer. Setting this to (uint32_t) -1 +     * will initialize this to the maximum value supported by server, +     * which is recommended. */ + +    uint32_t tlength; +    /**< Playback only: target length of the buffer. The server tries +     * to assure that at least tlength bytes are always available in +     * the per-stream server-side playback buffer. It is recommended +     * to set this to (uint32_t) -1, which will initialize this to a +     * value that is deemed sensible by the server. However, this +     * value will default to something like 2s, i.e. for applications +     * that have specific latency requirements this value should be +     * set to the maximum latency that the application can deal +     * with. When PA_STREAM_ADJUST_LATENCY is not set this value will +     * influence only the per-stream playback buffer size. When +     * PA_STREAM_ADJUST_LATENCY is set the overall latency of the sink +     * plus the playback buffer size is configured to this value. Set +     * PA_STREAM_ADJUST_LATENCY if you are interested in adjusting the +     * overall latency. Don't set it if you are interested in +     * configuring the server-sider per-stream playback buffer +     * size. */ + +    uint32_t prebuf; +    /**< Playback only: pre-buffering. The server does not start with +     * playback before at least prebug bytes are available in the +     * buffer. It is recommended to set this to (uint32_t) -1, which +     * will initialize this to the same value as tlength, whatever +     * that may be. Initialize to 0 to enable manual start/stop +     * control of the stream. This means that playback will not stop +     * on underrun and playback will not start automatically. Instead +     * pa_stream_corked() needs to be called explicitly. If you set +     * this value to 0 you should also set PA_STREAM_START_CORKED. */ + +    uint32_t minreq; +    /**< Playback only: minimum request. The server does not request +     * less than minreq bytes from the client, instead waits until the +     * buffer is free enough to request more bytes at once. It is +     * recommended to set this to (uint32_t) -1, which will initialize +     * this to a value that is deemed sensible by the server. This +     * should be set to a value that gives PulseAudio enough time to +     * move the data from the per-stream playback buffer into the +     * hardware playback buffer. */ + +    uint32_t fragsize; +    /**< Recording only: fragment size. The server sends data in +     * blocks of fragsize bytes size. Large values deminish +     * interactivity with other operations on the connection context +     * but decrease control overhead. It is recommended to set this to +     * (uint32_t) -1, which will initialize this to a value that is +     * deemed sensible by the server. However, this value will default +     * to something like 2s, i.e. for applications that have specific +     * latency requirements this value should be set to the maximum +     * latency that the application can deal with. If +     * PA_STREAM_ADJUST_LATENCY is set the overall source latency will +     * be adjusted according to this value. If it is not set the +     * source latency is left unmodified. */ +  } pa_buffer_attr;  /** Error values as used by pa_context_errno(). Use pa_strerror() to convert these values to human readable strings */ @@ -358,36 +353,84 @@ enum {  /** Subscription event mask, as used by pa_context_subscribe() */  typedef enum pa_subscription_mask { -    PA_SUBSCRIPTION_MASK_NULL = 0,               /**< No events */ -    PA_SUBSCRIPTION_MASK_SINK = 1,               /**< Sink events */ -    PA_SUBSCRIPTION_MASK_SOURCE = 2,             /**< Source events */ -    PA_SUBSCRIPTION_MASK_SINK_INPUT = 4,         /**< Sink input events */ -    PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT = 8,      /**< Source output events */ -    PA_SUBSCRIPTION_MASK_MODULE = 16,            /**< Module events */ -    PA_SUBSCRIPTION_MASK_CLIENT = 32,            /**< Client events */ -    PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 64,      /**< Sample cache events */ -    PA_SUBSCRIPTION_MASK_SERVER = 128,           /**< Other global server changes. */ -    PA_SUBSCRIPTION_MASK_AUTOLOAD = 256,         /**< Autoload table events. */ -    PA_SUBSCRIPTION_MASK_ALL = 511               /**< Catch all events */ +    PA_SUBSCRIPTION_MASK_NULL = 0x0000U, +    /**< No events */ + +    PA_SUBSCRIPTION_MASK_SINK = 0x0001U, +    /**< Sink events */ + +    PA_SUBSCRIPTION_MASK_SOURCE = 0x0002U, +    /**< Source events */ + +    PA_SUBSCRIPTION_MASK_SINK_INPUT = 0x0004U, +    /**< Sink input events */ + +    PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT = 0x0008U, +    /**< Source output events */ + +    PA_SUBSCRIPTION_MASK_MODULE = 0x0010U, +    /**< Module events */ + +    PA_SUBSCRIPTION_MASK_CLIENT = 0x0020U, +    /**< Client events */ + +    PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 0x0040U, +    /**< Sample cache events */ + +    PA_SUBSCRIPTION_MASK_SERVER = 0x0080U, +    /**< Other global server changes. */ + +    PA_SUBSCRIPTION_MASK_AUTOLOAD = 0x0100U, +    /**< Autoload table events. */ + +    PA_SUBSCRIPTION_MASK_ALL = 0x01ffU +    /**< Catch all events */  } pa_subscription_mask_t;  /** Subscription event types, as used by pa_context_subscribe() */  typedef enum pa_subscription_event_type { -    PA_SUBSCRIPTION_EVENT_SINK = 0,           /**< Event type: Sink */ -    PA_SUBSCRIPTION_EVENT_SOURCE = 1,         /**< Event type: Source */ -    PA_SUBSCRIPTION_EVENT_SINK_INPUT = 2,     /**< Event type: Sink input */ -    PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 3,  /**< Event type: Source output */ -    PA_SUBSCRIPTION_EVENT_MODULE = 4,         /**< Event type: Module */ -    PA_SUBSCRIPTION_EVENT_CLIENT = 5,         /**< Event type: Client */ -    PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 6,   /**< Event type: Sample cache item */ -    PA_SUBSCRIPTION_EVENT_SERVER = 7,         /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. */ -    PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8,       /**< Event type: Autoload table changes. */ -    PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 15, /**< A mask to extract the event type from an event value */ - -    PA_SUBSCRIPTION_EVENT_NEW = 0,            /**< A new object was created */ -    PA_SUBSCRIPTION_EVENT_CHANGE = 16,        /**< A property of the object was modified */ -    PA_SUBSCRIPTION_EVENT_REMOVE = 32,        /**< An object was removed */ -    PA_SUBSCRIPTION_EVENT_TYPE_MASK = 16+32   /**< A mask to extract the event operation from an event value */ +    PA_SUBSCRIPTION_EVENT_SINK = 0x0000U, +    /**< Event type: Sink */ + +    PA_SUBSCRIPTION_EVENT_SOURCE = 0x0001U, +    /**< Event type: Source */ + +    PA_SUBSCRIPTION_EVENT_SINK_INPUT = 0x0002U, +    /**< Event type: Sink input */ + +    PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 0x0003U, +    /**< Event type: Source output */ + +    PA_SUBSCRIPTION_EVENT_MODULE = 0x0004U, +    /**< Event type: Module */ + +    PA_SUBSCRIPTION_EVENT_CLIENT = 0x0005U, +    /**< Event type: Client */ + +    PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 0x0006U, +    /**< Event type: Sample cache item */ + +    PA_SUBSCRIPTION_EVENT_SERVER = 0x0007U, +    /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. */ + +    PA_SUBSCRIPTION_EVENT_AUTOLOAD = 0x0008U, +    /**< Event type: Autoload table changes. */ + +    PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 0x000FU, +    /**< A mask to extract the event type from an event value */ + +    PA_SUBSCRIPTION_EVENT_NEW = 0x0000U, +    /**< A new object was created */ + +    PA_SUBSCRIPTION_EVENT_CHANGE = 0x0010U, +    /**< A property of the object was modified */ + +    PA_SUBSCRIPTION_EVENT_REMOVE = 0x0020U, +    /**< An object was removed */ + +    PA_SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U, +    /**< A mask to extract the event operation from an event value */ +  } pa_subscription_event_type_t;  /** Return one if an event type t matches an event mask bitfield */ @@ -412,69 +455,71 @@ typedef enum pa_subscription_event_type {   * note that this structure can be extended as part of evolutionary   * API updates at any time in any new release.*/  typedef struct pa_timing_info { -    struct timeval timestamp; /**< The time when this timing info structure was current */ -    int synchronized_clocks;  /**< Non-zero if the local and the -                               * remote machine have synchronized -                               * clocks. If synchronized clocks are -                               * detected transport_usec becomes much -                               * more reliable. However, the code that -                               * detects synchronized clocks is very -                               * limited und unreliable itself. */ - -    pa_usec_t sink_usec;      /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */ -    pa_usec_t source_usec;    /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. */ -    pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. */ - -    int playing;              /**< Non-zero when the stream is -                               * currently not underrun and data is -                               * being passed on to the device. Only -                               * for playback streams. This field does -                               * not say whether the data is actually -                               * already being played. To determine -                               * this check whether since_underrun -                               * (converted to usec) is larger than -                               * sink_usec.*/ - -    int write_index_corrupt;  /**< Non-zero if write_index is not -                               * up-to-date because a local write -                               * command that corrupted it has been -                               * issued in the time since this latency -                               * info was current . Only write -                               * commands with SEEK_RELATIVE_ON_READ -                               * and SEEK_RELATIVE_END can corrupt -                               * write_index. */ -    int64_t write_index;      /**< Current write index into the -                               * playback buffer in bytes. Think twice before -                               * using this for seeking purposes: it -                               * might be out of date a the time you -                               * want to use it. Consider using -                               * PA_SEEK_RELATIVE instead.  */ - -    int read_index_corrupt;   /**< Non-zero if read_index is not -                               * up-to-date because a local pause or -                               * flush request that corrupted it has -                               * been issued in the time since this -                               * latency info was current. */ - -    int64_t read_index;       /**< Current read index into the -                               * playback buffer in bytes. Think twice before -                               * using this for seeking purposes: it -                               * might be out of date a the time you -                               * want to use it. Consider using -                               * PA_SEEK_RELATIVE_ON_READ -                               * instead. */ - -    pa_usec_t configured_sink_usec;   /**< The configured latency for -                                * the sink. \since 0.9.11 */ -    pa_usec_t configured_source_usec; /**< The configured latency for -                                * the source. \since 0.9.11 */ - -    int64_t since_underrun;    /**< Bytes that were handed to the sink -                                  since the last underrun happened, or -                                  since playback started again after -                                  the last underrun. playing will tell -                                  you which case it is. \since -                                  0.9.11 */ +    struct timeval timestamp; +    /**< The time when this timing info structure was current */ + +    int synchronized_clocks; +    /**< Non-zero if the local and the remote machine have +     * synchronized clocks. If synchronized clocks are detected +     * transport_usec becomes much more reliable. However, the code +     * that detects synchronized clocks is very limited und unreliable +     * itself. */ + +    pa_usec_t sink_usec; +    /**< Time in usecs a sample takes to be played on the sink. For +     * playback streams and record streams connected to a monitor +     * source. */ + +    pa_usec_t source_usec; +    /**< Time in usecs a sample takes from being recorded to being +     * delivered to the application. Only for record streams. */ + +    pa_usec_t transport_usec; +    /**< Estimated time in usecs a sample takes to be transferred +     * to/from the daemon. For both playback and record streams. */ + +    int playing; +    /**< Non-zero when the stream is currently not underrun and data +     * is being passed on to the device. Only for playback +     * streams. This field does not say whether the data is actually +     * already being played. To determine this check whether +     * since_underrun (converted to usec) is larger than sink_usec.*/ + +    int write_index_corrupt; +    /**< Non-zero if write_index is not up-to-date because a local +     * write command that corrupted it has been issued in the time +     * since this latency info was current . Only write commands with +     * SEEK_RELATIVE_ON_READ and SEEK_RELATIVE_END can corrupt +     * write_index. */ + +    int64_t write_index; +    /**< Current write index into the playback buffer in bytes. Think +     * twice before using this for seeking purposes: it might be out +     * of date a the time you want to use it. Consider using +     * PA_SEEK_RELATIVE instead. */ + +    int read_index_corrupt; +    /**< Non-zero if read_index is not up-to-date because a local +     * pause or flush request that corrupted it has been issued in the +     * time since this latency info was current. */ + +    int64_t read_index; +    /**< Current read index into the playback buffer in bytes. Think +     * twice before using this for seeking purposes: it might be out +     * of date a the time you want to use it. Consider using +     * PA_SEEK_RELATIVE_ON_READ instead. */ + +    pa_usec_t configured_sink_usec; +    /**< The configured latency for the sink. \since 0.9.11 */ + +    pa_usec_t configured_source_usec; +    /**< The configured latency for * the source. \since 0.9.11 */ + +    int64_t since_underrun; +    /**< Bytes that were handed to the sink since the last underrun +     * happened, or since playback started again after the last +     * underrun. playing will tell you which case it is. \since +     * 0.9.11 */  } pa_timing_info; @@ -486,45 +531,101 @@ typedef struct pa_timing_info {   * thread compatible way. You might have to do this in   * prefork/postfork. */  typedef struct pa_spawn_api { -    void (*prefork)(void);     /**< Is called just before the fork in the parent process. May be NULL. */ -    void (*postfork)(void);    /**< Is called immediately after the fork in the parent process. May be NULL.*/ -    void (*atfork)(void);      /**< Is called immediately after the -                                * fork in the child process. May be -                                * NULL. It is not safe to close all -                                * file descriptors in this function -                                * unconditionally, since a UNIX socket -                                * (created using socketpair()) is -                                * passed to the new process. */ +    void (*prefork)(void); +    /**< Is called just before the fork in the parent process. May be +     * NULL. */ + +    void (*postfork)(void); +    /**< Is called immediately after the fork in the parent +     * process. May be NULL.*/ + +    void (*atfork)(void); +    /**< Is called immediately after the fork in the child +     * process. May be NULL. It is not safe to close all file +     * descriptors in this function unconditionally, since a UNIX +     * socket (created using socketpair()) is passed to the new +     * process. */  } pa_spawn_api;  /** Seek type for pa_stream_write(). */  typedef enum pa_seek_mode { -    PA_SEEK_RELATIVE = 0,           /**< Seek relatively to the write index */ -    PA_SEEK_ABSOLUTE = 1,           /**< Seek relatively to the start of the buffer queue */ -    PA_SEEK_RELATIVE_ON_READ = 2,   /**< Seek relatively to the read index.  */ -    PA_SEEK_RELATIVE_END = 3        /**< Seek relatively to the current end of the buffer queue. */ +    PA_SEEK_RELATIVE = 0, +    /**< Seek relatively to the write index */ + +    PA_SEEK_ABSOLUTE = 1, +    /**< Seek relatively to the start of the buffer queue */ + +    PA_SEEK_RELATIVE_ON_READ = 2, +    /**< Seek relatively to the read index.  */ + +    PA_SEEK_RELATIVE_END = 3 +    /**< Seek relatively to the current end of the buffer queue. */  } pa_seek_mode_t;  /** Special sink flags. */  typedef enum pa_sink_flags { -    PA_SINK_HW_VOLUME_CTRL = 1,   /**< Supports hardware volume control */ -    PA_SINK_LATENCY = 2,          /**< Supports latency querying */ -    PA_SINK_HARDWARE = 4,         /**< Is a hardware sink of some kind, in contrast to "virtual"/software sinks \since 0.9.3 */ -    PA_SINK_NETWORK = 8,          /**< Is a networked sink of some kind. \since 0.9.7 */ -    PA_SINK_HW_MUTE_CTRL = 16,    /**< Supports hardware mute control \since 0.9.11 */ -    PA_SINK_DECIBEL_VOLUME = 32   /**< Volume can be translated to dB with pa_sw_volume_to_dB() \since 0.9.11 */ +    PA_SINK_HW_VOLUME_CTRL = 0x0001U, +    /**< Supports hardware volume control */ + +    PA_SINK_LATENCY = 0x0002U, +    /**< Supports latency querying */ + +    PA_SINK_HARDWARE = 0x0004U, +    /**< Is a hardware sink of some kind, in contrast to +     * "virtual"/software sinks \since 0.9.3 */ + +    PA_SINK_NETWORK = 0x0008U, +    /**< Is a networked sink of some kind. \since 0.9.7 */ + +    PA_SINK_HW_MUTE_CTRL = 0x0010U, +    /**< Supports hardware mute control \since 0.9.11 */ + +    PA_SINK_DECIBEL_VOLUME = 0x0020U +    /**< Volume can be translated to dB with pa_sw_volume_to_dB() +     * \since 0.9.11 */  } pa_sink_flags_t; +/** \cond fulldocs */ +#define PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL +#define PA_SINK_LATENCY PA_SINK_LATENCY +#define PA_SINK_HARDWARE PA_SINK_HARDWARE +#define PA_SINK_NETWORK PA_SINK_NETWORK +#define PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL +#define PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME +/** \endcond */ +  /** Special source flags.  */  typedef enum pa_source_flags { -    PA_SOURCE_HW_VOLUME_CTRL = 1,  /**< Supports hardware volume control */ -    PA_SOURCE_LATENCY = 2,         /**< Supports latency querying */ -    PA_SOURCE_HARDWARE = 4,        /**< Is a hardware source of some kind, in contrast to "virtual"/software source \since 0.9.3 */ -    PA_SOURCE_NETWORK = 8,         /**< Is a networked sink of some kind. \since 0.9.7 */ -    PA_SOURCE_HW_MUTE_CTRL = 16,   /**< Supports hardware mute control \since 0.9.11 */ -    PA_SOURCE_DECIBEL_VOLUME = 32  /**< Volume can be translated to dB with pa_sw_volume_to_dB() \since 0.9.11 */ +    PA_SOURCE_HW_VOLUME_CTRL = 0x0001U, +    /**< Supports hardware volume control */ + +    PA_SOURCE_LATENCY = 0x0002U, +    /**< Supports latency querying */ + +    PA_SOURCE_HARDWARE = 0x0004U, +    /**< Is a hardware source of some kind, in contrast to +     * "virtual"/software source \since 0.9.3 */ + +    PA_SOURCE_NETWORK = 0x0008U, +    /**< Is a networked sink of some kind. \since 0.9.7 */ + +    PA_SOURCE_HW_MUTE_CTRL = 0x0010U, +    /**< Supports hardware mute control \since 0.9.11 */ + +    PA_SOURCE_DECIBEL_VOLUME = 0x0020U +    /**< Volume can be translated to dB with pa_sw_volume_to_dB() +     * \since 0.9.11 */  } pa_source_flags_t; +/** \cond fulldocs */ +#define PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL +#define PA_SOURCE_LATENCY PA_SOURCE_LATENCY +#define PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE +#define PA_SOURCE_NETWORK PA_SOURCE_NETWORK +#define PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL +#define PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME +/** \endcond */ +  /** A generic free() like callback prototype */  typedef void (*pa_free_cb_t)(void *p); diff --git a/src/pulse/ext-stream-restore.h b/src/pulse/ext-stream-restore.h index a8eceaf1..2038eb4a 100644 --- a/src/pulse/ext-stream-restore.h +++ b/src/pulse/ext-stream-restore.h @@ -24,37 +24,49 @@  #include <pulse/context.h> +/** \file + * + * Routines for controlling module-stream-restore + */ +  PA_C_DECL_BEGIN +/** Stores information about one entry in the stream database that is + * maintained by module-stream-restore. \since 0.9.12 */  typedef struct pa_ext_stream_restore_info { -    const char *name; -    pa_channel_map channel_map; -    pa_cvolume volume; -    const char *device; -    int mute; +    const char *name;            /**< Identifier string of the stream. A string like "sink-input-by-role:" or similar followed by some arbitrary property value. */ +    pa_channel_map channel_map;  /**< The channel map for the volume field */ +    pa_cvolume volume;           /**< The volume of the stream when it was seen last, if applicable */ +    const char *device;          /**< The sink/source of the stream when it was last seen */ +    int mute;                    /**< The boolean mute state of the stream when it was last seen, if applicable */  } pa_ext_stream_restore_info; +/** Callback prototype for pa_ext_stream_restore_test(). \since 0.9.12 */  typedef void (*pa_ext_stream_restore_test_cb_t)(          pa_context *c,          uint32_t version,          void *userdata); +/** Test if this extension module is available in the server. \since 0.9.12 */  pa_operation *pa_ext_stream_restore_test(          pa_context *c,          pa_ext_stream_restore_test_cb_t cb,          void *userdata); +/** Callback prototype for pa_ext_stream_restore_read(). \since 0.9.12 */  typedef void (*pa_ext_stream_restore_read_cb_t)(          pa_context *c,          const pa_ext_stream_restore_info *info,          int eol,          void *userdata); +/** Read all entries from the stream database. \since 0.9.12 */  pa_operation *pa_ext_stream_restore_read(          pa_context *c,          pa_ext_stream_restore_read_cb_t cb,          void *userdata); +/** Store entries in the stream database. \since 0.9.12 */  pa_operation *pa_ext_stream_restore_write(          pa_context *c,          pa_update_mode_t mode, @@ -64,22 +76,27 @@ pa_operation *pa_ext_stream_restore_write(          pa_context_success_cb_t cb,          void *userdata); +/** Delete entries from the stream database. \since 0.9.12 */  pa_operation *pa_ext_stream_restore_delete(          pa_context *c,          const char *const s[],          pa_context_success_cb_t cb,          void *userdata); +/** Subscribe to changes in the stream database. \since 0.9.12 */  pa_operation *pa_ext_stream_restore_subscribe(          pa_context *c,          int enable,          pa_context_success_cb_t cb,          void *userdata); +/** Callback prototype for pa_ext_stream_restore_set_subscribe_cb(). \since 0.9.12 */  typedef void (*pa_ext_stream_restore_subscribe_cb_t)(          pa_context *c,          void *userdata); +/** Set the subscription callback that is called when + * pa_ext_stream_restore_subscribe() was called. \since 0.9.12 */  void pa_ext_stream_restore_set_subscribe_cb(          pa_context *c,          pa_ext_stream_restore_subscribe_cb_t cb, diff --git a/src/pulse/gccmacro.h b/src/pulse/gccmacro.h index e4062033..0533b109 100644 --- a/src/pulse/gccmacro.h +++ b/src/pulse/gccmacro.h @@ -93,4 +93,24 @@  #endif  #endif +#ifndef PA_GCC_ALLOC_SIZE +#if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3) +#define PA_GCC_ALLOC_SIZE(x) __attribute__ ((__alloc_size__(x))) +#define PA_GCC_ALLOC_SIZE2(x,y) __attribute__ ((__alloc_size__(x,y))) +#else +/** Macro for usage of GCC's alloc_size attribute */ +#define PA_GCC_ALLOC_SIZE(x) +#define PA_GCC_ALLOC_SIZE2(x,y) +#endif +#endif + +#ifndef PA_GCC_MALLOC +#ifdef __GNUCC__ +#define PA_GCC_MALLOC __attribute__ ((malloc)) +#else +/** Macro for usage of GCC's malloc attribute */ +#define PA_GCC_MALLOC +#endif +#endif +  #endif diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c index 6ddb0faa..5f5dc494 100644 --- a/src/pulse/glib-mainloop.c +++ b/src/pulse/glib-mainloop.c @@ -195,11 +195,11 @@ static void cleanup_defer_events(pa_glib_mainloop *g, int force) {  }  static gushort map_flags_to_glib(pa_io_event_flags_t flags) { -    return -        (flags & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | -        (flags & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0) | -        (flags & PA_IO_EVENT_ERROR ? G_IO_ERR : 0) | -        (flags & PA_IO_EVENT_HANGUP ? G_IO_HUP : 0); +    return (gushort) +        ((flags & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | +         (flags & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0) | +         (flags & PA_IO_EVENT_ERROR ? G_IO_ERR : 0) | +         (flags & PA_IO_EVENT_HANGUP ? G_IO_HUP : 0));  }  static pa_io_event_flags_t map_flags_from_glib(gushort flags) { @@ -425,7 +425,7 @@ static void glib_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_  /* quit() */ -static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) { +static void glib_quit(pa_mainloop_api*a, int retval) {      g_warning("quit() ignored"); @@ -536,7 +536,7 @@ static gboolean check_func(GSource *source) {      return FALSE;  } -static gboolean dispatch_func(GSource *source, PA_GCC_UNUSED GSourceFunc callback, PA_GCC_UNUSED gpointer userdata) { +static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer userdata) {      pa_glib_mainloop *g = (pa_glib_mainloop*) source;      pa_io_event *e; diff --git a/src/pulse/internal.h b/src/pulse/internal.h index bfe888ee..5fe4210e 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -64,7 +64,7 @@ struct pa_context {      uint32_t version;      uint32_t ctag;      uint32_t csyncid; -    uint32_t error; +    int error;      pa_context_state_t state;      pa_context_notify_cb_t state_callback; @@ -75,9 +75,9 @@ struct pa_context {      pa_mempool *mempool;      pa_bool_t is_local:1; -    pa_bool_t do_autospawn:1;      pa_bool_t do_shm:1; -    int autospawn_lock_fd; + +    pa_bool_t do_autospawn:1;      pa_spawn_api spawn_api;      pa_strlist *server_list; diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 4be2c62a..38091581 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -24,6 +24,8 @@  #include <config.h>  #endif +#include <string.h> +  #include <pulse/context.h>  #include <pulse/gccmacro.h> @@ -36,7 +38,7 @@  /*** Statistics ***/ -static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +static void context_stat_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      pa_stat_info i, *p = &i; @@ -79,7 +81,7 @@ pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdat  /*** Server Info ***/ -static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      pa_server_info i, *p = &i; @@ -127,7 +129,7 @@ pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb,  /*** Sink Info ***/ -static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      int eol = 1; @@ -248,7 +250,7 @@ pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name,  /*** Source info ***/ -static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      int eol = 1; @@ -369,7 +371,7 @@ pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name  /*** Client info ***/ -static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      int eol = 1; @@ -451,7 +453,7 @@ pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t  /*** Module info ***/ -static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      int eol = 1; @@ -530,7 +532,7 @@ pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t  /*** Sink input info ***/ -static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      int eol = 1; @@ -624,7 +626,7 @@ pa_operation* pa_context_get_sink_input_info_list(pa_context *c, void (*cb)(pa_c  /*** Source output info ***/ -static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      int eol = 1; @@ -954,7 +956,7 @@ pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name  /** Sample Cache **/ -static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      int eol = 1; @@ -1098,7 +1100,7 @@ pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_cont      return command_kill(c, PA_COMMAND_KILL_SOURCE_OUTPUT, idx, cb, userdata);  } -static void context_index_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +static void context_index_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      uint32_t idx; @@ -1159,7 +1161,7 @@ pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_s  /*** Autoload stuff ***/ -static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      int eol = 1; diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index ca79f5b7..087bd9f9 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -561,7 +561,7 @@ typedef enum pa_autoload_type {  typedef struct pa_autoload_info {      uint32_t index;               /**< Index of this autoload entry */      const char *name;             /**< Name of the sink or source */ -    pa_autoload_type_t type;   /**< Type of the autoload entry */ +    pa_autoload_type_t type;      /**< Type of the autoload entry */      const char *module;           /**< Module name to load */      const char *argument;         /**< Argument string for module */  } pa_autoload_info; diff --git a/src/pulse/mainloop-api.c b/src/pulse/mainloop-api.c index 4e3b135a..4b862f9a 100644 --- a/src/pulse/mainloop-api.c +++ b/src/pulse/mainloop-api.c @@ -51,7 +51,7 @@ static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata)      m->defer_free(e);  } -static void free_callback(pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event *e, void *userdata) { +static void free_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {      struct once_info *i = userdata;      pa_assert(m); diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c index e95968ae..d09f4b0a 100644 --- a/src/pulse/mainloop-signal.c +++ b/src/pulse/mainloop-signal.c @@ -90,7 +90,7 @@ static void dispatch(pa_mainloop_api*a, int sig) {          }  } -static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags_t f, PA_GCC_UNUSED void *userdata) { +static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata) {      ssize_t r;      int sig; diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 5823e280..60e5d1ff 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -57,7 +57,7 @@  struct pa_io_event {      pa_mainloop *mainloop; -    int dead; +    pa_bool_t dead:1;      int fd;      pa_io_event_flags_t events; @@ -72,9 +72,9 @@ struct pa_io_event {  struct pa_time_event {      pa_mainloop *mainloop; -    int dead; +    pa_bool_t dead:1; -    int enabled; +    pa_bool_t enabled:1;      struct timeval timeval;      pa_time_event_cb_t callback; @@ -86,9 +86,9 @@ struct pa_time_event {  struct pa_defer_event {      pa_mainloop *mainloop; -    int dead; +    pa_bool_t dead:1; -    int enabled; +    pa_bool_t enabled:1;      pa_defer_event_cb_t callback;      void *userdata; @@ -102,22 +102,24 @@ struct pa_mainloop {      PA_LLIST_HEAD(pa_time_event, time_events);      PA_LLIST_HEAD(pa_defer_event, defer_events); -    int n_enabled_defer_events, n_enabled_time_events, n_io_events; -    int io_events_please_scan, time_events_please_scan, defer_events_please_scan; +    unsigned n_enabled_defer_events, n_enabled_time_events, n_io_events; +    unsigned io_events_please_scan, time_events_please_scan, defer_events_please_scan; +    pa_bool_t rebuild_pollfds:1;      struct pollfd *pollfds;      unsigned max_pollfds, n_pollfds; -    int rebuild_pollfds;      int prepared_timeout;      pa_time_event *cached_next_time_event; -    int quit, retval;      pa_mainloop_api api; +    int retval; +    pa_bool_t quit:1; + +    pa_bool_t wakeup_requested:1;      int wakeup_pipe[2];      int wakeup_pipe_type; -    int wakeup_requested;      enum {          STATE_PASSIVE, @@ -133,11 +135,11 @@ struct pa_mainloop {  };  static short map_flags_to_libc(pa_io_event_flags_t flags) { -    return -        (flags & PA_IO_EVENT_INPUT ? POLLIN : 0) | -        (flags & PA_IO_EVENT_OUTPUT ? POLLOUT : 0) | -        (flags & PA_IO_EVENT_ERROR ? POLLERR : 0) | -        (flags & PA_IO_EVENT_HANGUP ? POLLHUP : 0); +    return (short) +        ((flags & PA_IO_EVENT_INPUT ? POLLIN : 0) | +         (flags & PA_IO_EVENT_OUTPUT ? POLLOUT : 0) | +         (flags & PA_IO_EVENT_ERROR ? POLLERR : 0) | +         (flags & PA_IO_EVENT_HANGUP ? POLLHUP : 0));  }  static pa_io_event_flags_t map_flags_from_libc(short flags) { @@ -169,7 +171,7 @@ static pa_io_event* mainloop_io_new(      e = pa_xnew(pa_io_event, 1);      e->mainloop = m; -    e->dead = 0; +    e->dead = FALSE;      e->fd = fd;      e->events = events; @@ -194,13 +196,13 @@ static pa_io_event* mainloop_io_new(                      SELECT_TYPE_ARG5 &tv) == -1) &&               (WSAGetLastError() == WSAENOTSOCK)) {              pa_log_warn("Cannot monitor non-socket file descriptors."); -            e->dead = 1; +            e->dead = TRUE;          }      }  #endif      PA_LLIST_PREPEND(pa_io_event, m->io_events, e); -    m->rebuild_pollfds = 1; +    m->rebuild_pollfds = TRUE;      m->n_io_events ++;      pa_mainloop_wakeup(m); @@ -220,7 +222,7 @@ static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) {      if (e->pollfd)          e->pollfd->events = map_flags_to_libc(events);      else -        e->mainloop->rebuild_pollfds = 1; +        e->mainloop->rebuild_pollfds = TRUE;      pa_mainloop_wakeup(e->mainloop);  } @@ -229,11 +231,11 @@ static void mainloop_io_free(pa_io_event *e) {      pa_assert(e);      pa_assert(!e->dead); -    e->dead = 1; +    e->dead = TRUE;      e->mainloop->io_events_please_scan ++;      e->mainloop->n_io_events --; -    e->mainloop->rebuild_pollfds = 1; +    e->mainloop->rebuild_pollfds = TRUE;      pa_mainloop_wakeup(e->mainloop);  } @@ -262,9 +264,9 @@ static pa_defer_event* mainloop_defer_new(      e = pa_xnew(pa_defer_event, 1);      e->mainloop = m; -    e->dead = 0; +    e->dead = FALSE; -    e->enabled = 1; +    e->enabled = TRUE;      m->n_enabled_defer_events++;      e->callback = callback; @@ -297,13 +299,13 @@ static void mainloop_defer_free(pa_defer_event *e) {      pa_assert(e);      pa_assert(!e->dead); -    e->dead = 1; +    e->dead = TRUE;      e->mainloop->defer_events_please_scan ++;      if (e->enabled) {          pa_assert(e->mainloop->n_enabled_defer_events > 0);          e->mainloop->n_enabled_defer_events--; -        e->enabled = 0; +        e->enabled = FALSE;      }  } @@ -333,7 +335,7 @@ static pa_time_event* mainloop_time_new(      e = pa_xnew(pa_time_event, 1);      e->mainloop = m; -    e->dead = 0; +    e->dead = FALSE;      if ((e->enabled = !!tv)) {          e->timeval = *tv; @@ -388,13 +390,13 @@ static void mainloop_time_free(pa_time_event *e) {      pa_assert(e);      pa_assert(!e->dead); -    e->dead = 1; +    e->dead = TRUE;      e->mainloop->time_events_please_scan ++;      if (e->enabled) {          pa_assert(e->mainloop->n_enabled_time_events > 0);          e->mainloop->n_enabled_time_events--; -        e->enabled = 0; +        e->enabled = FALSE;      }      if (e->mainloop->cached_next_time_event == e) @@ -462,7 +464,7 @@ pa_mainloop *pa_mainloop_new(void) {      pa_make_fd_nonblock(m->wakeup_pipe[1]);      pa_make_fd_cloexec(m->wakeup_pipe[0]);      pa_make_fd_cloexec(m->wakeup_pipe[1]); -    m->wakeup_requested = 0; +    m->wakeup_requested = FALSE;      PA_LLIST_HEAD_INIT(pa_io_event, m->io_events);      PA_LLIST_HEAD_INIT(pa_time_event, m->time_events); @@ -476,9 +478,10 @@ pa_mainloop *pa_mainloop_new(void) {      m->pollfds = NULL;      m->max_pollfds = m->n_pollfds = 0; -    m->rebuild_pollfds = 1; +    m->rebuild_pollfds = TRUE; -    m->quit = m->retval = 0; +    m->quit = FALSE; +    m->retval = 0;      m->api = vtable;      m->api.userdata = m; @@ -492,7 +495,7 @@ pa_mainloop *pa_mainloop_new(void) {      return m;  } -static void cleanup_io_events(pa_mainloop *m, int force) { +static void cleanup_io_events(pa_mainloop *m, pa_bool_t force) {      pa_io_event *e;      e = m->io_events; @@ -515,7 +518,7 @@ static void cleanup_io_events(pa_mainloop *m, int force) {              pa_xfree(e); -            m->rebuild_pollfds = 1; +            m->rebuild_pollfds = TRUE;          }          e = n; @@ -524,7 +527,7 @@ static void cleanup_io_events(pa_mainloop *m, int force) {      pa_assert(m->io_events_please_scan == 0);  } -static void cleanup_time_events(pa_mainloop *m, int force) { +static void cleanup_time_events(pa_mainloop *m, pa_bool_t force) {      pa_time_event *e;      e = m->time_events; @@ -545,7 +548,7 @@ static void cleanup_time_events(pa_mainloop *m, int force) {              if (!e->dead && e->enabled) {                  pa_assert(m->n_enabled_time_events > 0);                  m->n_enabled_time_events--; -                e->enabled = 0; +                e->enabled = FALSE;              }              if (e->destroy_callback) @@ -560,7 +563,7 @@ static void cleanup_time_events(pa_mainloop *m, int force) {      pa_assert(m->time_events_please_scan == 0);  } -static void cleanup_defer_events(pa_mainloop *m, int force) { +static void cleanup_defer_events(pa_mainloop *m, pa_bool_t force) {      pa_defer_event *e;      e = m->defer_events; @@ -581,7 +584,7 @@ static void cleanup_defer_events(pa_mainloop *m, int force) {              if (!e->dead && e->enabled) {                  pa_assert(m->n_enabled_defer_events > 0);                  m->n_enabled_defer_events--; -                e->enabled = 0; +                e->enabled = FALSE;              }              if (e->destroy_callback) @@ -600,9 +603,9 @@ static void cleanup_defer_events(pa_mainloop *m, int force) {  void pa_mainloop_free(pa_mainloop* m) {      pa_assert(m); -    cleanup_io_events(m, 1); -    cleanup_defer_events(m, 1); -    cleanup_time_events(m, 1); +    cleanup_io_events(m, TRUE); +    cleanup_defer_events(m, TRUE); +    cleanup_time_events(m, TRUE);      pa_xfree(m->pollfds); @@ -615,13 +618,13 @@ static void scan_dead(pa_mainloop *m) {      pa_assert(m);      if (m->io_events_please_scan) -        cleanup_io_events(m, 0); +        cleanup_io_events(m, FALSE);      if (m->time_events_please_scan) -        cleanup_time_events(m, 0); +        cleanup_time_events(m, FALSE);      if (m->defer_events_please_scan) -        cleanup_defer_events(m, 0); +        cleanup_defer_events(m, FALSE);  }  static void rebuild_pollfds(pa_mainloop *m) { @@ -662,7 +665,7 @@ static void rebuild_pollfds(pa_mainloop *m) {          m->n_pollfds++;      } -    m->rebuild_pollfds = 0; +    m->rebuild_pollfds = FALSE;  }  static int dispatch_pollfds(pa_mainloop *m) { @@ -948,7 +951,7 @@ int pa_mainloop_run(pa_mainloop *m, int *retval) {  void pa_mainloop_quit(pa_mainloop *m, int retval) {      pa_assert(m); -    m->quit = 1; +    m->quit = TRUE;      m->retval = retval;      pa_mainloop_wakeup(m);  } diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c index 9e0549ea..93bc0034 100644 --- a/src/pulse/proplist.c +++ b/src/pulse/proplist.c @@ -280,7 +280,7 @@ char *pa_proplist_to_string(pa_proplist *p) {              char *c;              pa_assert_se(pa_proplist_get(p, key, &value, &nbytes) == 0); -            c = pa_xnew(char, nbytes*2+1); +            c = pa_xmalloc(nbytes*2+1);              pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1);              pa_strbuf_printf(buf, "%s = hex:%s\n", key, c); diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index 39d53303..c23ef238 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -168,9 +168,19 @@ 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_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. */ -    PA_UPDATE_REPLACE /*< Merge new property list into the existing one, replacing all old entries that share a common key with  the new property list. */ +    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 +     *  any old entries if they share a common key with the new +     *  property list. */ + +    PA_UPDATE_REPLACE +    /*< 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;  /** Merge property list "other" into "p", adhering the merge mode as diff --git a/src/pulse/sample.c b/src/pulse/sample.c index 93da2465..29501595 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -80,6 +80,16 @@ size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) {      return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * pa_frame_size(spec);  } +pa_sample_spec* pa_sample_spec_init(pa_sample_spec *spec) { +    pa_assert(spec); + +    spec->format = PA_SAMPLE_INVALID; +    spec->rate = 0; +    spec->channels = 0; + +    return spec; +} +  int pa_sample_spec_valid(const pa_sample_spec *spec) {      pa_assert(spec); @@ -131,7 +141,7 @@ char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) {      pa_init_i18n();      if (!pa_sample_spec_valid(spec)) -        pa_snprintf(s, l, _("Invalid")); +        pa_snprintf(s, l, _("(invalid)"));      else          pa_snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate); diff --git a/src/pulse/sample.h b/src/pulse/sample.h index 2680cf7e..3c7dd0e7 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -120,17 +120,38 @@ PA_C_DECL_BEGIN  /** Sample format */  typedef enum pa_sample_format { -    PA_SAMPLE_U8,              /**< Unsigned 8 Bit PCM */ -    PA_SAMPLE_ALAW,            /**< 8 Bit a-Law */ -    PA_SAMPLE_ULAW,            /**< 8 Bit mu-Law */ -    PA_SAMPLE_S16LE,           /**< Signed 16 Bit PCM, little endian (PC) */ -    PA_SAMPLE_S16BE,           /**< Signed 16 Bit PCM, big endian */ -    PA_SAMPLE_FLOAT32LE,       /**< 32 Bit IEEE floating point, little endian, range -1 to 1 */ -    PA_SAMPLE_FLOAT32BE,       /**< 32 Bit IEEE floating point, big endian, range -1 to 1 */ -    PA_SAMPLE_S32LE,           /**< Signed 32 Bit PCM, little endian (PC) */ -    PA_SAMPLE_S32BE,           /**< Signed 32 Bit PCM, big endian (PC) */ -    PA_SAMPLE_MAX,             /**< Upper limit of valid sample types */ -    PA_SAMPLE_INVALID = -1     /**< An invalid value */ +    PA_SAMPLE_U8, +    /**< Unsigned 8 Bit PCM */ + +    PA_SAMPLE_ALAW, +    /**< 8 Bit a-Law */ + +    PA_SAMPLE_ULAW, +    /**< 8 Bit mu-Law */ + +    PA_SAMPLE_S16LE, +    /**< Signed 16 Bit PCM, little endian (PC) */ + +    PA_SAMPLE_S16BE, +    /**< Signed 16 Bit PCM, big endian */ + +    PA_SAMPLE_FLOAT32LE, +    /**< 32 Bit IEEE floating point, little endian, range -1 to 1 */ + +    PA_SAMPLE_FLOAT32BE, +    /**< 32 Bit IEEE floating point, big endian, range -1 to 1 */ + +    PA_SAMPLE_S32LE, +    /**< Signed 32 Bit PCM, little endian (PC) */ + +    PA_SAMPLE_S32BE, +    /**< Signed 32 Bit PCM, big endian (PC) */ + +    PA_SAMPLE_MAX, +    /**< Upper limit of valid sample types */ + +    PA_SAMPLE_INVALID = -1 +    /**< An invalid value */  } pa_sample_format_t;  #ifdef WORDS_BIGENDIAN @@ -164,11 +185,29 @@ typedef enum pa_sample_format {  /** A Shortcut for PA_SAMPLE_FLOAT32NE */  #define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32NE +/** \cond fulldocs */ +/* Allow clients to check with #ifdef for thse sample formats */ +#define PA_SAMPLE_U8 PA_SAMPLE_U8 +#define PA_SAMPLE_ALAW PA_SAMPLE_ALAW +#define PA_SAMPLE_ULAW PA_SAMPLE_ULAW +#define PA_SAMPLE_S16LE PA_SAMPLE_S16LE +#define PA_SAMPLE_S16BE PA_SAMPLE_S16BE +#define PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE +#define PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE +#define PA_SAMPLE_S32LE PA_SAMPLE_S32LE +#define PA_SAMPLE_S32BE PA_SAMPLE_S32BE +/** \endcond */ +  /** A sample format and attribute specification */  typedef struct pa_sample_spec { -    pa_sample_format_t format;     /**< The sample format */ -    uint32_t rate;                 /**< The sample rate. (e.g. 44100) */ -    uint8_t channels;              /**< Audio channels. (1 for mono, 2 for stereo, ...) */ +    pa_sample_format_t format; +    /**< The sample format */ + +    uint32_t rate; +    /**< The sample rate. (e.g. 44100) */ + +    uint8_t channels; +    /**< Audio channels. (1 for mono, 2 for stereo, ...) */  } pa_sample_spec;  /** Type for usec specifications (unsigned). Always 64 bit. */ @@ -183,12 +222,21 @@ size_t pa_frame_size(const pa_sample_spec *spec) PA_GCC_PURE;  /** Return the size of a sample with the specific sample type */  size_t pa_sample_size(const pa_sample_spec *spec) PA_GCC_PURE; -/** Calculate the time the specified bytes take to play with the specified sample type */ +/** Calculate the time the specified bytes take to play with the + * specified sample type. The return value will always be rounded + * down for non-integral return values. */  pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) PA_GCC_PURE; -/** Calculates the number of bytes that are required for the specified time. \since 0.9 */ +/** Calculates the number of bytes that are required for the specified + * time. The return value will always be rounded down for non-integral + * return values. \since 0.9 */  size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) PA_GCC_PURE; +/** Initialize the specified sample spec and return a pointer to + * it. The sample spec will have a defined state but + * pa_sample_spec_valid() will fail for it. \since 0.9.13 */ +pa_sample_spec* pa_sample_spec_init(pa_sample_spec *spec); +  /** Return non-zero when the sample type specification is valid */  int pa_sample_spec_valid(const pa_sample_spec *spec) PA_GCC_PURE; @@ -201,7 +249,11 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) PA_GCC_PURE;  /** Parse a sample format text. Inverse of pa_sample_format_to_string() */  pa_sample_format_t pa_parse_sample_format(const char *format) PA_GCC_PURE; -/** Maximum required string length for pa_sample_spec_snprint() */ +/** Maximum required string length for + * pa_sample_spec_snprint(). Please note that this value can change + * with any release without warning and without being considered API + * or ABI breakage. You should not use this definition anywhere where + * it might become part of an ABI. */  #define PA_SAMPLE_SPEC_SNPRINT_MAX 32  /** Pretty print a sample type specification to a string */ diff --git a/src/pulse/scache.c b/src/pulse/scache.c index 5e31e7af..fd3b9876 100644 --- a/src/pulse/scache.c +++ b/src/pulse/scache.c @@ -47,6 +47,7 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) {      PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY(s->context, length > 0, PA_ERR_INVALID); +    PA_CHECK_VALIDITY(s->context, length == (size_t) (uint32_t) length, PA_ERR_INVALID);      if (!(name = pa_proplist_gets(s->proplist, PA_PROP_EVENT_ID)))          name = pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME); @@ -63,7 +64,7 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) {      pa_tagstruct_puts(t, name);      pa_tagstruct_put_sample_spec(t, &s->sample_spec);      pa_tagstruct_put_channel_map(t, &s->channel_map); -    pa_tagstruct_putu32(t, length); +    pa_tagstruct_putu32(t, (uint32_t) length);      if (s->context->version >= 13) {          pa_init_proplist(s->proplist); diff --git a/src/pulse/simple.c b/src/pulse/simple.c index 51160ad7..79e39ebb 100644 --- a/src/pulse/simple.c +++ b/src/pulse/simple.c @@ -272,7 +272,7 @@ int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) {          if (l > length)              l = length; -        r = pa_stream_write(p->stream, data, l, NULL, 0, PA_SEEK_RELATIVE); +        r = pa_stream_write(p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);          CHECK_SUCCESS_GOTO(p, rerror, r >= 0, unlock_and_fail);          data = (const uint8_t*) data + l; diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 585518f0..c0ae4ac2 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -86,7 +86,7 @@ pa_stream *pa_stream_new_with_proplist(      pa_assert(PA_REFCNT_VALUE(c) >= 1);      PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID); -    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE || ss->format != PA_SAMPLE_S32NE), PA_ERR_NOTSUPPORTED); +    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED);      PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);      PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID); @@ -124,7 +124,7 @@ pa_stream *pa_stream_new_with_proplist(       * what older PA versions provided. */      s->buffer_attr.maxlength = (uint32_t) -1; -    s->buffer_attr.tlength = pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */ +    s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */      s->buffer_attr.minreq = (uint32_t) -1;      s->buffer_attr.prebuf = (uint32_t) -1;      s->buffer_attr.fragsize = (uint32_t) -1; @@ -315,7 +315,7 @@ static void request_auto_timing_update(pa_stream *s, pa_bool_t force) {      }  } -void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_context *c = userdata;      pa_stream *s;      uint32_t channel; @@ -382,7 +382,7 @@ static void check_smoother_status(pa_stream *s, pa_bool_t aposteriori, pa_bool_t       * if prebuf is non-zero! */  } -void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_context *c = userdata;      pa_stream *s;      uint32_t channel; @@ -479,7 +479,7 @@ finish:      pa_context_unref(c);  } -void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_context *c = userdata;      pa_stream *s;      uint32_t channel; @@ -557,13 +557,13 @@ void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag,      request_auto_timing_update(s, TRUE);      if (s->started_callback) -        s->started_callback(s, s->suspended_userdata); +        s->started_callback(s, s->started_userdata);  finish:      pa_context_unref(c);  } -void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_stream *s;      pa_context *c = userdata;      uint32_t bytes, channel; @@ -598,7 +598,7 @@ finish:      pa_context_unref(c);  } -void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_stream *s;      pa_context *c = userdata;      uint32_t channel; @@ -670,7 +670,7 @@ static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) {      request_auto_timing_update(s, TRUE);  } -static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { +static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *tv, void *userdata) {      pa_stream *s = userdata;      pa_assert(s); @@ -694,7 +694,7 @@ static void create_stream_complete(pa_stream *s) {      if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) {          struct timeval tv;          pa_gettimeofday(&tv); -        tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */ +        tv.tv_usec += (suseconds_t) LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */          pa_assert(!s->auto_timing_update_event);          s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s); @@ -722,7 +722,7 @@ static void automatic_buffer_attr(pa_stream *s, pa_buffer_attr *attr, const pa_s          attr->maxlength = 4*1024*1024; /* 4MB is the maximum queue length PulseAudio <= 0.9.9 supported. */      if (attr->tlength == (uint32_t) -1) -        attr->tlength = pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */ +        attr->tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */      if (attr->minreq == (uint32_t) -1)          attr->minreq = (attr->tlength)/5; /* Ask for more data when there are only 200ms left in the playback buffer */ @@ -734,7 +734,7 @@ static void automatic_buffer_attr(pa_stream *s, pa_buffer_attr *attr, const pa_s          attr->fragsize = attr->tlength; /* Pass data to the app only when the buffer is filled up once */  } -void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_stream *s = userdata;      pa_assert(pd); @@ -865,6 +865,7 @@ static int create_stream(      pa_tagstruct *t;      uint32_t tag; +    pa_bool_t volume_set = FALSE;      pa_assert(s);      pa_assert(PA_REFCNT_VALUE(s) >= 1); @@ -885,7 +886,10 @@ static int create_stream(                                                PA_STREAM_VARIABLE_RATE|                                                PA_STREAM_PEAK_DETECT|                                                PA_STREAM_START_MUTED| -                                              PA_STREAM_ADJUST_LATENCY)), PA_ERR_INVALID); +                                              PA_STREAM_ADJUST_LATENCY| +                                              PA_STREAM_EARLY_REQUESTS| +                                              PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND| +                                              PA_STREAM_START_UNMUTED)), PA_ERR_INVALID);      PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED);      PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED); @@ -898,6 +902,7 @@ static int create_stream(      PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_RECORD || !(flags & (PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID);      PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID);      PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID); +    PA_CHECK_VALIDITY(s->context, (flags & (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS)) != (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS), PA_ERR_INVALID);      pa_stream_ref(s); @@ -930,7 +935,7 @@ static int create_stream(      t = pa_tagstruct_command(              s->context, -            s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM, +            (uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM),              &tag);      if (s->context->version < 13) @@ -957,6 +962,8 @@ static int create_stream(                  PA_TAG_U32, s->syncid,                  PA_TAG_INVALID); +        volume_set = !!volume; +          if (!volume)              volume = pa_cvolume_reset(&cv, s->sample_spec.channels); @@ -994,6 +1001,22 @@ static int create_stream(              pa_tagstruct_putu32(t, s->direct_on_input);      } +    if (s->context->version >= 14) { + +        if (s->direction == PA_STREAM_PLAYBACK) +            pa_tagstruct_put_boolean(t, volume_set); + +        pa_tagstruct_put_boolean(t, flags & PA_STREAM_EARLY_REQUESTS); +    } + +    if (s->context->version >= 15) { + +        if (s->direction == PA_STREAM_PLAYBACK) +            pa_tagstruct_put_boolean(t, flags & (PA_STREAM_START_MUTED|PA_STREAM_START_UNMUTED)); + +        pa_tagstruct_put_boolean(t, flags & PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND); +    } +      pa_pstream_send_tagstruct(s->context->pstream, t);      pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL); @@ -1093,7 +1116,7 @@ int pa_stream_write(          free_cb((void*) data);      if (length < s->requested_bytes) -        s->requested_bytes -= length; +        s->requested_bytes -= (uint32_t) length;      else          s->requested_bytes = 0; @@ -1107,10 +1130,10 @@ int pa_stream_write(              if (seek == PA_SEEK_ABSOLUTE) {                  s->write_index_corrections[s->current_write_index_correction].corrupt = FALSE;                  s->write_index_corrections[s->current_write_index_correction].absolute = TRUE; -                s->write_index_corrections[s->current_write_index_correction].value = offset + length; +                s->write_index_corrections[s->current_write_index_correction].value = offset + (int64_t) length;              } else if (seek == PA_SEEK_RELATIVE) {                  if (!s->write_index_corrections[s->current_write_index_correction].corrupt) -                    s->write_index_corrections[s->current_write_index_correction].value += offset + length; +                    s->write_index_corrections[s->current_write_index_correction].value += offset + (int64_t) length;              } else                  s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;          } @@ -1120,10 +1143,10 @@ int pa_stream_write(              if (seek == PA_SEEK_ABSOLUTE) {                  s->timing_info.write_index_corrupt = FALSE; -                s->timing_info.write_index = offset + length; +                s->timing_info.write_index = offset + (int64_t) length;              } else if (seek == PA_SEEK_RELATIVE) {                  if (!s->timing_info.write_index_corrupt) -                    s->timing_info.write_index += offset + length; +                    s->timing_info.write_index += offset + (int64_t) length;              } else                  s->timing_info.write_index_corrupt = TRUE;          } @@ -1173,7 +1196,7 @@ int pa_stream_drop(pa_stream *s) {      /* Fix the simulated local read index */      if (s->timing_info_valid && !s->timing_info.read_index_corrupt) -        s->timing_info.read_index += s->peek_memchunk.length; +        s->timing_info.read_index += (int64_t) s->peek_memchunk.length;      pa_assert(s->peek_data);      pa_memblock_release(s->peek_memchunk.memblock); @@ -1257,7 +1280,9 @@ static pa_usec_t calc_time(pa_stream *s, pa_bool_t ignore_transport) {                  usec -= s->timing_info.sink_usec;          } -    } else if (s->direction == PA_STREAM_RECORD) { +    } else { +        pa_assert(s->direction == PA_STREAM_RECORD); +          /* The last byte written into the server side queue had           * this time value associated */          usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec); @@ -1340,7 +1365,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,          i->read_index_corrupt = FALSE;          i->playing = (int) playing; -        i->since_underrun = playing ? playing_for : underrun_for; +        i->since_underrun = (int64_t) (playing ? playing_for : underrun_for);          pa_gettimeofday(&now); @@ -1418,7 +1443,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,              /* Read index correction */              if (!i->read_index_corrupt) -                i->read_index -= pa_memblockq_get_length(o->stream->record_memblockq); +                i->read_index -= (int64_t) pa_memblockq_get_length(o->stream->record_memblockq);          }          /* Update smoother */ @@ -1435,7 +1460,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,                   * speakers. Since we follow that timing here, we need                   * to try to fix this up */ -                su = pa_bytes_to_usec(i->since_underrun, &o->stream->sample_spec); +                su = pa_bytes_to_usec((uint64_t) i->since_underrun, &o->stream->sample_spec);                  if (su < i->sink_usec)                      x += i->sink_usec - su; @@ -1494,7 +1519,7 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t      t = pa_tagstruct_command(              s->context, -            s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY, +            (uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY),              &tag);      pa_tagstruct_putu32(t, s->channel);      pa_tagstruct_put_timeval(t, pa_gettimeofday(&now)); @@ -1517,7 +1542,7 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t      return o;  } -void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_stream *s = userdata;      pa_assert(pd); @@ -1557,8 +1582,8 @@ int pa_stream_disconnect(pa_stream *s) {      t = pa_tagstruct_command(              s->context, -            s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_DELETE_PLAYBACK_STREAM : -            (s->direction == PA_STREAM_RECORD ? PA_COMMAND_DELETE_RECORD_STREAM : PA_COMMAND_DELETE_UPLOAD_STREAM), +            (uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_DELETE_PLAYBACK_STREAM : +                        (s->direction == PA_STREAM_RECORD ? PA_COMMAND_DELETE_RECORD_STREAM : PA_COMMAND_DELETE_UPLOAD_STREAM)),              &tag);      pa_tagstruct_putu32(t, s->channel);      pa_pstream_send_tagstruct(s->context->pstream, t); @@ -1667,7 +1692,7 @@ void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void      s->started_userdata = userdata;  } -void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      int success = 1; @@ -1715,7 +1740,7 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi      t = pa_tagstruct_command(              s->context, -            s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CORK_PLAYBACK_STREAM : PA_COMMAND_CORK_RECORD_STREAM, +            (uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CORK_PLAYBACK_STREAM : PA_COMMAND_CORK_RECORD_STREAM),              &tag);      pa_tagstruct_putu32(t, s->channel);      pa_tagstruct_put_boolean(t, !!b); @@ -1760,7 +1785,7 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use      PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); -    if (!(o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata))) +    if (!(o = stream_send_simple_command(s, (uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM), cb, userdata)))          return NULL;      if (s->direction == PA_STREAM_PLAYBACK) { @@ -1836,7 +1861,7 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe      if (s->context->version >= 13) {          pa_proplist *p = pa_proplist_new(); -        pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name); +        pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);          o = pa_stream_proplist_update(s, PA_UPDATE_REPLACE, p, cb, userdata);          pa_proplist_free(p);      } else { @@ -1846,7 +1871,7 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe          o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);          t = pa_tagstruct_command(                  s->context, -                s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME, +                (uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME),                  &tag);          pa_tagstruct_putu32(t, s->channel);          pa_tagstruct_puts(t, name); @@ -1932,7 +1957,7 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) {      if (cindex < 0)          cindex = 0; -    c = pa_bytes_to_usec(cindex, &s->sample_spec); +    c = pa_bytes_to_usec((uint64_t) cindex, &s->sample_spec);      if (s->direction == PA_STREAM_PLAYBACK)          *r_usec = time_counter_diff(s, c, t, negative); @@ -1948,7 +1973,7 @@ const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) {      PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);      PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); -    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->timing_info_valid, PA_ERR_BADSTATE); +    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->timing_info_valid, PA_ERR_NODATA);      return &s->timing_info;  } @@ -1978,7 +2003,7 @@ const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) {      return &s->buffer_attr;  } -static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      int success = 1; @@ -1995,7 +2020,6 @@ static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command,          success = 0;      } else { -          if (o->stream->direction == PA_STREAM_PLAYBACK) {              if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 ||                  pa_tagstruct_getu32(t, &o->stream->buffer_attr.tlength) < 0 || @@ -2012,6 +2036,20 @@ static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command,              }          } +        if (o->stream->context->version >= 13) { +            pa_usec_t usec; + +            if (pa_tagstruct_get_usec(t, &usec) < 0) { +                pa_context_fail(o->context, PA_ERR_PROTOCOL); +                goto finish; +            } + +            if (o->stream->direction == PA_STREAM_RECORD) +                o->stream->timing_info.configured_source_usec = usec; +            else +                o->stream->timing_info.configured_sink_usec = usec; +        } +          if (!pa_tagstruct_eof(t)) {              pa_context_fail(o->context, PA_ERR_PROTOCOL);              goto finish; @@ -2046,7 +2084,7 @@ pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr      t = pa_tagstruct_command(              s->context, -            s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR : PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR, +            (uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR : PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR),              &tag);      pa_tagstruct_putu32(t, s->channel); @@ -2065,6 +2103,9 @@ pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr      if (s->context->version >= 13)          pa_tagstruct_put_boolean(t, !!(s->flags & PA_STREAM_ADJUST_LATENCY)); +    if (s->context->version >= 14) +        pa_tagstruct_put_boolean(t, !!(s->flags & PA_STREAM_EARLY_REQUESTS)); +      pa_pstream_send_tagstruct(s->context->pstream, t);      pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_set_buffer_attr_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); @@ -2120,7 +2161,7 @@ int pa_stream_is_corked(pa_stream *s) {      return s->corked;  } -static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_operation *o = userdata;      int success = 1; @@ -2177,7 +2218,7 @@ pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_strea      t = pa_tagstruct_command(              s->context, -            s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE : PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE, +            (uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE : PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE),              &tag);      pa_tagstruct_putu32(t, s->channel);      pa_tagstruct_putu32(t, rate); @@ -2205,7 +2246,7 @@ pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_      t = pa_tagstruct_command(              s->context, -            s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST : PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST, +            (uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST : PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST),              &tag);      pa_tagstruct_putu32(t, s->channel);      pa_tagstruct_putu32(t, (uint32_t) mode); @@ -2238,7 +2279,7 @@ pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[],      t = pa_tagstruct_command(              s->context, -            s->direction == PA_STREAM_RECORD ? PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST : PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST, +            (uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST : PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST),              &tag);      pa_tagstruct_putu32(t, s->channel); diff --git a/src/pulse/stream.h b/src/pulse/stream.h index 2a8f7a8b..6cb363c8 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -473,7 +473,7 @@ void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, vo  /** Set the callback function that is called when a the server starts   * playback after an underrun or on initial startup. This only informs - * that audio is flowing again, it is no indication that audio startet + * that audio is flowing again, it is no indication that audio started   * to reach the speakers already. (Only for playback streams). \since   * 0.9.11 */  void pa_stream_set_started_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c index b8d3be89..e12d1446 100644 --- a/src/pulse/subscribe.c +++ b/src/pulse/subscribe.c @@ -34,7 +34,7 @@  #include "subscribe.h" -void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_context *c = userdata;      pa_subscription_event_type_t e;      uint32_t idx; diff --git a/src/pulse/timeval.c b/src/pulse/timeval.c index 9708a735..376cf13c 100644 --- a/src/pulse/timeval.c +++ b/src/pulse/timeval.c @@ -90,13 +90,13 @@ pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) {      }      /* Calculate the second difference*/ -    r = ((pa_usec_t) a->tv_sec - b->tv_sec) * PA_USEC_PER_SEC; +    r = ((pa_usec_t) a->tv_sec - (pa_usec_t) b->tv_sec) * PA_USEC_PER_SEC;      /* Calculate the microsecond difference */      if (a->tv_usec > b->tv_usec) -        r += ((pa_usec_t) a->tv_usec - b->tv_usec); +        r += ((pa_usec_t) a->tv_usec - (pa_usec_t) b->tv_usec);      else if (a->tv_usec < b->tv_usec) -        r -= ((pa_usec_t) b->tv_usec - a->tv_usec); +        r -= ((pa_usec_t) b->tv_usec - (pa_usec_t) a->tv_usec);      return r;  } @@ -132,7 +132,7 @@ struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) {      pa_assert(tv);      secs = (unsigned long) (v/PA_USEC_PER_SEC); -    tv->tv_sec += secs; +    tv->tv_sec += (time_t) secs;      v -= ((pa_usec_t) secs) * PA_USEC_PER_SEC;      tv->tv_usec += (suseconds_t) v; @@ -140,7 +140,7 @@ struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) {      /* Normalize */      while ((unsigned) tv->tv_usec >= PA_USEC_PER_SEC) {          tv->tv_sec++; -        tv->tv_usec -= PA_USEC_PER_SEC; +        tv->tv_usec -= (suseconds_t) PA_USEC_PER_SEC;      }      return tv; @@ -151,14 +151,14 @@ struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v) {      pa_assert(tv);      secs = (unsigned long) (v/PA_USEC_PER_SEC); -    tv->tv_sec -= secs; +    tv->tv_sec -= (time_t) secs;      v -= ((pa_usec_t) secs) * PA_USEC_PER_SEC;      if (tv->tv_usec >= (suseconds_t) v)          tv->tv_usec -= (suseconds_t) v;      else {          tv->tv_sec --; -        tv->tv_usec = tv->tv_usec + PA_USEC_PER_SEC - v; +        tv->tv_usec += (suseconds_t) (PA_USEC_PER_SEC - v);      }      return tv; @@ -167,8 +167,8 @@ struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v) {  struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v) {      pa_assert(tv); -    tv->tv_sec = v / PA_USEC_PER_SEC; -    tv->tv_usec = v % PA_USEC_PER_SEC; +    tv->tv_sec = (time_t) (v / PA_USEC_PER_SEC); +    tv->tv_usec = (suseconds_t) (v % PA_USEC_PER_SEC);      return tv;  } diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c index 119be542..7671be46 100644 --- a/src/pulse/utf8.c +++ b/src/pulse/utf8.c @@ -64,24 +64,24 @@  #define FILTER_CHAR '_' -static inline int is_unicode_valid(uint32_t ch) { +static inline pa_bool_t is_unicode_valid(uint32_t ch) {      if (ch >= 0x110000) /* End of unicode space */ -        return 0; +        return FALSE;      if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */ -        return 0; +        return FALSE;      if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */ -        return 0; +        return FALSE;      if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */ -        return 0; +        return FALSE; -    return 1; +    return TRUE;  } -static inline int is_continuation_char(uint8_t ch) { +static inline pa_bool_t is_continuation_char(uint8_t ch) {      if ((ch & 0xc0) != 0x80) /* 10xxxxxx */ -        return 0; -    return 1; +        return FALSE; +    return TRUE;  }  static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) { @@ -109,17 +109,17 @@ static char* utf8_validate(const char *str, char *output) {              if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */                  size = 2;                  min = 128; -                val = *p & 0x1e; +                val = (uint32_t) (*p & 0x1e);                  goto ONE_REMAINING;              } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/                  size = 3;                  min = (1 << 11); -                val = *p & 0x0f; +                val = (uint32_t) (*p & 0x0f);                  goto TWO_REMAINING;              } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */                  size = 4;                  min = (1 << 16); -                val = *p & 0x07; +                val = (uint32_t) (*p & 0x07);              } else {                  size = 1;                  goto error; @@ -149,7 +149,7 @@ ONE_REMAINING:                  goto error;              if (o) { -                memcpy(o, last, size); +                memcpy(o, last, (size_t) size);                  o += size - 1;              } @@ -189,7 +189,7 @@ char* pa_utf8_filter (const char *str) {      char *new_str;      pa_assert(str); -    new_str = pa_xnew(char, strlen(str) + 1); +    new_str = pa_xmalloc(strlen(str) + 1);      return utf8_validate(str, new_str);  } @@ -212,7 +212,7 @@ static char* iconv_simple(const char *str, const char *to, const char *from) {          return NULL;      inlen = len = strlen(str) + 1; -    new_str = pa_xnew(char, len); +    new_str = pa_xmalloc(len);      for (;;) {          inbuf = (ICONV_CONST char*) str; /* Brain dead prototype for iconv() */ diff --git a/src/pulse/util.c b/src/pulse/util.c index f785a2e9..b20ea46a 100644 --- a/src/pulse/util.c +++ b/src/pulse/util.c @@ -95,12 +95,15 @@ char *pa_get_user_name(char *s, size_t l) {  #elif defined(OS_IS_WIN32) /* HAVE_PWD_H */          DWORD size = sizeof(buf); -        if (!GetUserName(buf, &size)) +        if (!GetUserName(buf, &size)) { +            errno = ENOENT;              return NULL; +        }          p = buf;  #else /* HAVE_PWD_H */ +          return NULL;  #endif /* HAVE_PWD_H */      } @@ -138,6 +141,8 @@ char *pa_get_home_dir(char *s, size_t l) {          return pa_strlcpy(s, e, l);  #ifdef HAVE_PWD_H + +    errno = 0;  #ifdef HAVE_GETPWUID_R      if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {  #else @@ -145,11 +150,16 @@ char *pa_get_home_dir(char *s, size_t l) {          * that do not support getpwuid_r. */      if ((r = getpwuid(getuid())) == NULL) {  #endif +        if (!errno) +            errno = ENOENT; +          return NULL;      }      return pa_strlcpy(s, r->pw_dir, l);  #else /* HAVE_PWD_H */ + +    errno = ENOENT;      return NULL;  #endif  } @@ -200,6 +210,7 @@ char *pa_get_binary_name(char *s, size_t l) {      }  #endif +    errno = ENOENT;      return NULL;  } @@ -249,8 +260,8 @@ int pa_msleep(unsigned long t) {  #elif defined(HAVE_NANOSLEEP)      struct timespec ts; -    ts.tv_sec = t/1000; -    ts.tv_nsec = (t % 1000) * 1000000; +    ts.tv_sec = (time_t) (t/1000UL); +    ts.tv_nsec = (long) ((t % 1000UL) * 1000000UL);      return nanosleep(&ts, NULL);  #else diff --git a/src/pulse/volume.c b/src/pulse/volume.c index 625eb19a..99a85f44 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -26,6 +26,7 @@  #include <stdio.h>  #include <string.h> +#include <pulse/i18n.h>  #include <pulsecore/core-util.h>  #include <pulsecore/macro.h> @@ -46,6 +47,19 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {      return 1;  } +pa_cvolume* pa_cvolume_init(pa_cvolume *a) { +    unsigned c; + +    pa_assert(a); + +    a->channels = 0; + +    for (c = 0; c < PA_CHANNELS_MAX; c++) +        a->values[c] = (pa_volume_t) -1; + +    return a; +} +  pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {      int i; @@ -53,7 +67,7 @@ pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {      pa_assert(channels > 0);      pa_assert(channels <= PA_CHANNELS_MAX); -    a->channels = channels; +    a->channels = (uint8_t) channels;      for (i = 0; i < a->channels; i++)          a->values[i] = v; @@ -74,8 +88,29 @@ pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {      return (pa_volume_t) sum;  } +pa_volume_t pa_cvolume_max(const pa_cvolume *a) { +    pa_volume_t m = 0; +    int i; +    pa_assert(a); + +    for (i = 0; i < a->channels; i++) +        if (a->values[i] > m) +            m = a->values[i]; + +    return m; +} +  pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) { -    return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a)* pa_sw_volume_to_linear(b)); +    return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a) * pa_sw_volume_to_linear(b)); +} + +pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) { +    double v = pa_sw_volume_to_linear(b); + +    if (v <= 0) +        return 0; + +    return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a) / v);  }  #define USER_DECIBEL_RANGE 60 @@ -84,7 +119,7 @@ pa_volume_t pa_sw_volume_from_dB(double dB) {      if (isinf(dB) < 0 || dB <= -USER_DECIBEL_RANGE)          return PA_VOLUME_MUTED; -    return (pa_volume_t) ((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM); +    return (pa_volume_t) lrint((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM);  }  double pa_sw_volume_to_dB(pa_volume_t v) { @@ -110,18 +145,25 @@ double pa_sw_volume_to_linear(pa_volume_t v) {      if (v == PA_VOLUME_MUTED)          return 0; -    return pow(10, pa_sw_volume_to_dB(v)/20); +    return pow(10.0, pa_sw_volume_to_dB(v)/20.0);  }  char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {      unsigned channel; -    int first = 1; +    pa_bool_t first = TRUE;      char *e;      pa_assert(s);      pa_assert(l > 0);      pa_assert(c); +    pa_init_i18n(); + +    if (!pa_cvolume_valid(c)) { +        pa_snprintf(s, l, _("(invalid)")); +        return s; +    } +      *(e = s) = 0;      for (channel = 0; channel < c->channels && l > 1; channel++) { @@ -131,7 +173,38 @@ char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {                        (c->values[channel]*100)/PA_VOLUME_NORM);          e = strchr(e, 0); -        first = 0; +        first = FALSE; +    } + +    return s; +} + +char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c) { +    unsigned channel; +    pa_bool_t first = TRUE; +    char *e; + +    pa_assert(s); +    pa_assert(l > 0); +    pa_assert(c); + +    pa_init_i18n(); + +    if (!pa_cvolume_valid(c)) { +        pa_snprintf(s, l, _("(invalid)")); +        return s; +    } + +    *(e = s) = 0; + +    for (channel = 0; channel < c->channels && l > 1; channel++) { +        l -= pa_snprintf(e, l, "%s%u: %0.2f dB", +                      first ? "" : " ", +                      channel, +                      pa_sw_volume_to_dB(c->values[channel])); + +        e = strchr(e, 0); +        first = FALSE;      }      return s; @@ -156,24 +229,41 @@ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const      pa_assert(a);      pa_assert(b); -    for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) { +    for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) +        dest->values[i] = pa_sw_volume_multiply(a->values[i], b->values[i]); -        dest->values[i] = pa_sw_volume_multiply( -            i < a->channels ? a->values[i] : PA_VOLUME_NORM, -            i < b->channels ? b->values[i] : PA_VOLUME_NORM); -    } +    dest->channels = (uint8_t) i; -    dest->channels = i; +    return dest; +} + +pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { +    unsigned i; + +    pa_assert(dest); +    pa_assert(a); +    pa_assert(b); + +    for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) +        dest->values[i] = pa_sw_volume_divide(a->values[i], b->values[i]); + +    dest->channels = (uint8_t) i;      return dest;  }  int pa_cvolume_valid(const pa_cvolume *v) { +    unsigned c; +      pa_assert(v);      if (v->channels <= 0 || v->channels > PA_CHANNELS_MAX)          return 0; +    for (c = 0; c < v->channels; c++) +        if (v->values[c] == (pa_volume_t) -1) +            return 0; +      return 1;  } @@ -261,3 +351,17 @@ pa_cvolume *pa_cvolume_remap(pa_cvolume *v, pa_channel_map *from, pa_channel_map      *v = result;      return v;  } + +int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) { + +    pa_assert(v); +    pa_assert(ss); + +    if (!pa_cvolume_valid(v)) +        return 0; + +    if (!pa_sample_spec_valid(ss)) +        return 0; + +    return v->channels == ss->channels; +} diff --git a/src/pulse/volume.h b/src/pulse/volume.h index 4fdbf658..75051af5 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -62,7 +62,7 @@   *   * The functions described above are only valid when used with   * software volumes. Hence it is usually a better idea to treat all - * volume values as opaque with a range from PA_VOLUME_MUTE (0%) to + * volume values as opaque with a range from PA_VOLUME_MUTED (0%) to   * PA_VOLUME_NORM (100%) and to refrain from any calculations with   * them.   * @@ -116,6 +116,11 @@ typedef struct pa_cvolume {  /** Return non-zero when *a == *b */  int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) PA_GCC_PURE; +/** Initialize the specified volume and return a pointer to + * it. The sample spec will have a defined state but + * pa_cvolume_valid() will fail for it. \since 0.9.13 */ +pa_cvolume* pa_cvolume_init(pa_cvolume *a); +  /** Set the volume of all channels to PA_VOLUME_NORM */  #define pa_cvolume_reset(a, n) pa_cvolume_set((a), (n), PA_VOLUME_NORM) @@ -125,15 +130,32 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) PA_GCC_PURE;  /** Set the volume of all channels to the specified parameter */  pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v); -/** Maximum length of the strings returned by pa_cvolume_snprint() */ -#define PA_CVOLUME_SNPRINT_MAX 64 +/** Maximum length of the strings returned by + * pa_cvolume_snprint(). Please note that this value can change with + * any release without warning and without being considered API or ABI + * breakage. You should not use this definition anywhere where it + * might become part of an ABI.*/ +#define PA_CVOLUME_SNPRINT_MAX 320  /** Pretty print a volume structure */  char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c); +/** Maximum length of the strings returned by + * pa_cvolume_snprint_dB(). Please note that this value can change with + * any release without warning and without being considered API or ABI + * breakage. You should not use this definition anywhere where it + * might become part of an ABI. \since 0.9.13 */ +#define PA_SW_CVOLUME_SNPRINT_DB_MAX 448 + +/** Pretty print a volume structure but show dB values. \since 0.9.13 */ +char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c); +  /** Return the average volume of all channels */  pa_volume_t pa_cvolume_avg(const pa_cvolume *a) PA_GCC_PURE; +/** Return the maximum volume of all channels. \since 0.9.12 */ +pa_volume_t pa_cvolume_max(const pa_cvolume *a) PA_GCC_PURE; +  /** Return TRUE when the passed cvolume structure is valid, FALSE otherwise */  int pa_cvolume_valid(const pa_cvolume *v) PA_GCC_PURE; @@ -146,12 +168,25 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) PA_GCC_PURE  /** Return 1 if the specified volume has all channels on normal level */  #define pa_cvolume_is_norm(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_NORM) -/** Multiply two volumes specifications, return the result. This uses PA_VOLUME_NORM as neutral element of multiplication. This is only valid for software volumes! */ +/** Multiply two volume specifications, return the result. This uses + * PA_VOLUME_NORM as neutral element of multiplication. This is only + * valid for software volumes! */  pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) PA_GCC_CONST; -/** Multiply to per-channel volumes and return the result in *dest. This is only valid for software volumes! */ +/** Multiply two per-channel volumes and return the result in + * *dest. This is only valid for software volumes! */  pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); +/** Divide two volume specifications, return the result. This uses + * PA_VOLUME_NORM as neutral element of division. This is only valid + * for software volumes! If a division by zero is tried the result + * will be 0. \since 0.9.13 */ +pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) PA_GCC_CONST; + +/** Multiply to per-channel volumes and return the result in + * *dest. This is only valid for software volumes! \since 0.9.13 */ +pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); +  /** Convert a decibel value to a volume. This is only valid for software volumes! */  pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST; @@ -174,6 +209,10 @@ double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST;  /** Remap a volume from one channel mapping to a different channel mapping. \since 0.9.12 */  pa_cvolume *pa_cvolume_remap(pa_cvolume *v, pa_channel_map *from, pa_channel_map *to); +/** Return non-zero if the specified volume is compatible with + * the specified sample spec. \since 0.9.13 */ +int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) PA_GCC_PURE; +  PA_C_DECL_END  #endif diff --git a/src/pulse/xmalloc.c b/src/pulse/xmalloc.c index 90237013..c570e40f 100644 --- a/src/pulse/xmalloc.c +++ b/src/pulse/xmalloc.c @@ -36,7 +36,7 @@  #include "xmalloc.h"  /* Make sure not to allocate more than this much memory. */ -#define MAX_ALLOC_SIZE (1024*1024*20) /* 20MB */ +#define MAX_ALLOC_SIZE (1024*1024*96) /* 96MB */  /* #undef malloc */  /* #undef free */ @@ -46,7 +46,7 @@  static void oom(void) PA_GCC_NORETURN; -/** called in case of an OOM situation. Prints an error message and +/* called in case of an OOM situation. Prints an error message and   * exits */  static void oom(void) {      static const char e[] = "Not enough memory\n"; @@ -113,7 +113,7 @@ char *pa_xstrndup(const char *s, size_t l) {          return NULL;      if ((e = memchr(s, 0, l))) -        return pa_xmemdup(s, e-s+1); +        return pa_xmemdup(s, (size_t) (e-s+1));      r = pa_xmalloc(l+1);      memcpy(r, s, l); diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h index c453138b..b2643588 100644 --- a/src/pulse/xmalloc.h +++ b/src/pulse/xmalloc.h @@ -26,7 +26,9 @@  #include <stdlib.h>  #include <limits.h>  #include <assert.h> +  #include <pulse/cdecl.h> +#include <pulse/gccmacro.h>  /** \file   * Memory allocation functions. @@ -35,52 +37,58 @@  PA_C_DECL_BEGIN  /** Allocate the specified number of bytes, just like malloc() does. However, in case of OOM, terminate */ -void* pa_xmalloc(size_t l); +void* pa_xmalloc(size_t l) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE(1);  /** Same as pa_xmalloc(), but initialize allocated memory to 0 */ -void *pa_xmalloc0(size_t l); +void *pa_xmalloc0(size_t l) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE(1);  /**  The combination of pa_xmalloc() and realloc() */ -void *pa_xrealloc(void *ptr, size_t size); +void *pa_xrealloc(void *ptr, size_t size) PA_GCC_ALLOC_SIZE(2);  /** Free allocated memory */  void pa_xfree(void *p);  /** Duplicate the specified string, allocating memory with pa_xmalloc() */ -char *pa_xstrdup(const char *s); +char *pa_xstrdup(const char *s) PA_GCC_MALLOC;  /** Duplicate the specified string, but truncate after l characters */ -char *pa_xstrndup(const char *s, size_t l); +char *pa_xstrndup(const char *s, size_t l) PA_GCC_MALLOC;  /** Duplicate the specified memory block */ -void* pa_xmemdup(const void *p, size_t l); +void* pa_xmemdup(const void *p, size_t l) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE(2);  /** Internal helper for pa_xnew() */ -static inline void* pa_xnew_internal(unsigned n, size_t k) { +static void* _pa_xnew_internal(size_t n, size_t k) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE2(1,2); + +static inline void* _pa_xnew_internal(size_t n, size_t k) {      assert(n < INT_MAX/k);      return pa_xmalloc(n*k);  }  /** Allocate n new structures of the specified type. */ -#define pa_xnew(type, n) ((type*) pa_xnew_internal((n), sizeof(type))) +#define pa_xnew(type, n) ((type*) _pa_xnew_internal((n), sizeof(type)))  /** Internal helper for pa_xnew0() */ -static inline void* pa_xnew0_internal(unsigned n, size_t k) { +static void* _pa_xnew0_internal(size_t n, size_t k) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE2(1,2); + +static inline void* _pa_xnew0_internal(size_t n, size_t k) {      assert(n < INT_MAX/k);      return pa_xmalloc0(n*k);  }  /** Same as pa_xnew() but set the memory to zero */ -#define pa_xnew0(type, n) ((type*) pa_xnew0_internal((n), sizeof(type))) +#define pa_xnew0(type, n) ((type*) _pa_xnew0_internal((n), sizeof(type)))  /** Internal helper for pa_xnew0() */ -static inline void* pa_xnewdup_internal(const void *p, unsigned n, size_t k) { +static void* _pa_xnewdup_internal(const void *p, size_t n, size_t k) PA_GCC_MALLOC PA_GCC_ALLOC_SIZE2(2,3); + +static inline void* _pa_xnewdup_internal(const void *p, size_t n, size_t k) {      assert(n < INT_MAX/k);      return pa_xmemdup(p, n*k);  }  /** Same as pa_xnew() but set the memory to zero */ -#define pa_xnewdup(type, p, n) ((type*) pa_xnewdup_internal((p), (n), sizeof(type))) +#define pa_xnewdup(type, p, n) ((type*) _pa_xnewdup_internal((p), (n), sizeof(type)))  PA_C_DECL_END diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c index 03e9f0df..f64931a5 100644 --- a/src/pulsecore/asyncq.c +++ b/src/pulsecore/asyncq.c @@ -71,7 +71,7 @@ PA_STATIC_FLIST_DECLARE(localq, 0, pa_xfree);  #define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq)))) -static int reduce(pa_asyncq *l, int value) { +static unsigned reduce(pa_asyncq *l, unsigned value) {      return value & (unsigned) (l->size - 1);  } @@ -132,7 +132,7 @@ void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) {  }  static int push(pa_asyncq*l, void *p, pa_bool_t wait) { -    int idx; +    unsigned idx;      pa_atomic_ptr_t *cells;      pa_assert(l); @@ -220,7 +220,7 @@ void pa_asyncq_post(pa_asyncq*l, void *p) {  }  void* pa_asyncq_pop(pa_asyncq*l, pa_bool_t wait) { -    int idx; +    unsigned idx;      void *ret;      pa_atomic_ptr_t *cells; @@ -263,7 +263,7 @@ int pa_asyncq_read_fd(pa_asyncq *q) {  }  int pa_asyncq_read_before_poll(pa_asyncq *l) { -    int idx; +    unsigned idx;      pa_atomic_ptr_t *cells;      pa_assert(l); @@ -280,8 +280,6 @@ int pa_asyncq_read_before_poll(pa_asyncq *l) {          if (pa_fdsem_before_poll(l->write_fdsem) >= 0)              return 0;      } - -    return 0;  }  void pa_asyncq_read_after_poll(pa_asyncq *l) { diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h index a91c4d56..9c58c661 100644 --- a/src/pulsecore/atomic.h +++ b/src/pulsecore/atomic.h @@ -23,6 +23,8 @@    USA.  ***/ +#include <pulsecore/macro.h> +  /*   * atomic_ops guarantees us that sizeof(AO_t) == sizeof(void*).  It is   * not guaranteed however, that sizeof(AO_t) == sizeof(size_t). @@ -35,7 +37,7 @@   * On gcc >= 4.1 we use the builtin atomic functions. otherwise we use   * libatomic_ops   */ -# +  #ifndef PACKAGE  #error "Please include config.h before including this file!"  #endif @@ -80,8 +82,8 @@ static inline int pa_atomic_dec(pa_atomic_t *a) {      return pa_atomic_sub(a, 1);  } -/* Returns non-zero when the operation was successful. */ -static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { +/* Returns TRUE when the operation was successful. */ +static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {      return __sync_bool_compare_and_swap(&a->value, old_i, new_i);  } @@ -101,13 +103,13 @@ static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {      __sync_synchronize();  } -static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { +static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {      return __sync_bool_compare_and_swap(&a->value, (long) old_p, (long) new_p);  }  #elif defined(__GNUC__) && (defined(__amd64__) || defined(__x86_64__)) -#error "The native atomic operations implementation for AMD64 has not been tested. libatomic_ops is known to not work properly on AMD64 and your gcc version is too old for the gcc-builtin atomic ops support. You have three options now: make the native atomic operations implementation for AMD64 work, fix libatomic_ops, or upgrade your GCC." +#warn "The native atomic operations implementation for AMD64 has not been tested thoroughly. libatomic_ops is known to not work properly on AMD64 and your gcc version is too old for the gcc-builtin atomic ops support. You have three options now: test the native atomic operations implementation for AMD64, fix libatomic_ops, or upgrade your GCC."  /* Addapted from glibc */ @@ -147,14 +149,14 @@ static inline int pa_atomic_dec(pa_atomic_t *a) {      return pa_atomic_sub(a, 1);  } -static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { +static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {      int result;      __asm__ __volatile__ ("lock; cmpxchgl %2, %1"                            : "=a" (result), "=m" (a->value)                            : "r" (new_i), "m" (a->value), "0" (old_i)); -    return result == oldval; +    return result == old_i;  }  typedef struct pa_atomic_ptr { @@ -171,14 +173,14 @@ static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {      a->value = (unsigned long) p;  } -static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { +static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {      void *result;      __asm__ __volatile__ ("lock; cmpxchgq %q2, %1"                            : "=a" (result), "=m" (a->value)                            : "r" (new_p), "m" (a->value), "0" (old_p)); -    return result; +    return result == old_p;  }  #elif defined(ATOMIC_ARM_INLINE_ASM) @@ -255,7 +257,7 @@ static inline int pa_atomic_dec(pa_atomic_t *a) {      return pa_atomic_sub(a, 1);  } -static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { +static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {      unsigned long not_equal, not_exclusive;      pa_memory_barrier(); @@ -289,7 +291,7 @@ static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {      pa_memory_barrier();  } -static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { +static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {      unsigned long not_equal, not_exclusive;      pa_memory_barrier(); @@ -377,8 +379,8 @@ static inline int pa_atomic_dec(pa_atomic_t *a) {      return pa_atomic_sub(a, 1);  } -/* Returns non-zero when the operation was successful. */ -static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { +/* Returns TRUE when the operation was successful. */ +static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {      pa_bool_t failed;      do {        failed = !!__kernel_cmpxchg(old_i, new_i, &a->value); @@ -402,7 +404,7 @@ static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {      pa_memory_barrier();  } -static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { +static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {      pa_bool_t failed;      do {          failed = !!__kernel_cmpxchg_u((unsigned long) old_p, (unsigned long) new_p, &a->value); @@ -420,7 +422,7 @@ typedef struct pa_atomic {      volatile AO_t value;  } pa_atomic_t; -#define PA_ATOMIC_INIT(v) { .value = (v) } +#define PA_ATOMIC_INIT(v) { .value = (AO_t) (v) }  static inline int pa_atomic_load(const pa_atomic_t *a) {      return (int) AO_load_full((AO_t*) &a->value); @@ -431,23 +433,23 @@ static inline void pa_atomic_store(pa_atomic_t *a, int i) {  }  static inline int pa_atomic_add(pa_atomic_t *a, int i) { -    return AO_fetch_and_add_full(&a->value, (AO_t) i); +    return (int) AO_fetch_and_add_full(&a->value, (AO_t) i);  }  static inline int pa_atomic_sub(pa_atomic_t *a, int i) { -    return AO_fetch_and_add_full(&a->value, (AO_t) -i); +    return (int) AO_fetch_and_add_full(&a->value, (AO_t) -i);  }  static inline int pa_atomic_inc(pa_atomic_t *a) { -    return AO_fetch_and_add1_full(&a->value); +    return (int) AO_fetch_and_add1_full(&a->value);  }  static inline int pa_atomic_dec(pa_atomic_t *a) { -    return AO_fetch_and_sub1_full(&a->value); +    return (int) AO_fetch_and_sub1_full(&a->value);  } -static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { -    return AO_compare_and_swap_full(&a->value, old_i, new_i); +static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { +    return AO_compare_and_swap_full(&a->value, (unsigned long) old_i, (unsigned long) new_i);  }  typedef struct pa_atomic_ptr { @@ -464,7 +466,7 @@ static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {      AO_store_full(&a->value, (AO_t) p);  } -static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { +static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {      return AO_compare_and_swap_full(&a->value, (AO_t) old_p, (AO_t) new_p);  } diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c index f3f40f80..b122feee 100644 --- a/src/pulsecore/authkey.c +++ b/src/pulsecore/authkey.c @@ -54,8 +54,8 @@ static int generate(int fd, void *ret_data, size_t length) {      pa_random(ret_data, length); -    lseek(fd, 0, SEEK_SET); -    (void) ftruncate(fd, 0); +    lseek(fd, (off_t) 0, SEEK_SET); +    (void) ftruncate(fd, (off_t) 0);      if ((r = pa_loop_write(fd, ret_data, length, NULL)) < 0 || (size_t) r != length) {          pa_log("Failed to write cookie file: %s", pa_cstrerror(errno)); @@ -88,7 +88,7 @@ static int load(const char *fn, void *data, size_t length) {      if ((fd = open(fn, O_RDWR|O_CREAT|O_BINARY|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {          if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY|O_NOCTTY)) < 0) { -            pa_log("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno)); +            pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));              goto finish;          } else              writable = 0; @@ -105,7 +105,7 @@ static int load(const char *fn, void *data, size_t length) {          pa_log_debug("Got %d bytes from cookie file '%s', expected %d", (int) r, fn, (int) length);          if (!writable) { -            pa_log("Unable to write cookie to read only file"); +            pa_log_warn("Unable to write cookie to read-only file");              goto finish;          } @@ -140,7 +140,7 @@ int pa_authkey_load(const char *path, void *data, size_t length) {      pa_assert(length > 0);      if ((ret = load(path, data, length)) < 0) -        pa_log("Failed to load authorization key '%s': %s", path, (ret < 0) ? pa_cstrerror(errno) : "File corrupt"); +        pa_log_warn("Failed to load authorization key '%s': %s", path, (ret < 0) ? pa_cstrerror(errno) : "File corrupt");      return ret;  } @@ -206,7 +206,7 @@ int pa_authkey_save(const char *fn, const void *data, size_t length) {          return -2;      if ((fd = open(p, O_RDWR|O_CREAT|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) { -        pa_log("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno)); +        pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));          goto finish;      } diff --git a/src/pulsecore/autoload.c b/src/pulsecore/autoload.c index 26c294b2..8c84cee5 100644 --- a/src/pulsecore/autoload.c +++ b/src/pulsecore/autoload.c @@ -158,7 +158,7 @@ void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) {      e->in_action = 0;  } -static void free_func(void *p, PA_GCC_UNUSED void *userdata) { +static void free_func(void *p, void *userdata) {      pa_autoload_entry *e = p;      pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);      entry_free(e); diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index a80933fa..b5ff98db 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -117,6 +117,10 @@ static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa  static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);  static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);  static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);  /* A method table for all available commands */ @@ -167,6 +171,10 @@ static const struct command commands[] = {      { "suspend-sink",            pa_cli_command_suspend_sink,       "Suspend sink (args: index|name, bool)", 3},      { "suspend-source",          pa_cli_command_suspend_source,     "Suspend source (args: index|name, bool)", 3},      { "suspend",                 pa_cli_command_suspend,            "Suspend all sinks and all sources (args: bool)", 2}, +    { "set-log-level",           pa_cli_command_log_level,          "Change the log level (args: numeric level)", 2}, +    { "set-log-meta",            pa_cli_command_log_meta,           "Show source code location in log messages (args: bool)", 2}, +    { "set-log-time",            pa_cli_command_log_time,           "Show timestamps in log messages (args: bool)", 2}, +    { "set-log-backtrace",       pa_cli_command_log_backtrace,      "Show bakctrace in log messages (args: frames)", 2},      { NULL, NULL, NULL, 0 }  }; @@ -318,22 +326,22 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b      pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",                       (unsigned) pa_atomic_load(&stat->n_allocated), -                     pa_bytes_snprint(s, sizeof(s), (size_t) pa_atomic_load(&stat->allocated_size))); +                     pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->allocated_size)));      pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",                       (unsigned) pa_atomic_load(&stat->n_accumulated), -                     pa_bytes_snprint(s, sizeof(s), (size_t) pa_atomic_load(&stat->accumulated_size))); +                     pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->accumulated_size)));      pa_strbuf_printf(buf, "Memory blocks imported from other processes: %u, size: %s.\n",                       (unsigned) pa_atomic_load(&stat->n_imported), -                     pa_bytes_snprint(s, sizeof(s), (size_t) pa_atomic_load(&stat->imported_size))); +                     pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->imported_size)));      pa_strbuf_printf(buf, "Memory blocks exported to other processes: %u, size: %s.\n",                       (unsigned) pa_atomic_load(&stat->n_exported), -                     pa_bytes_snprint(s, sizeof(s), (size_t) pa_atomic_load(&stat->exported_size))); +                     pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->exported_size)));      pa_strbuf_printf(buf, "Total sample cache size: %s.\n", -                     pa_bytes_snprint(s, sizeof(s), pa_scache_total_size(c))); +                     pa_bytes_snprint(s, sizeof(s), (unsigned) pa_scache_total_size(c)));      pa_strbuf_printf(buf, "Default sample spec: %s\n",                       pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec)); @@ -1203,6 +1211,102 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p      return 0;  } +static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { +    const char *m; +    uint32_t level; + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    if (!(m = pa_tokenizer_get(t, 1))) { +        pa_strbuf_puts(buf, "You need to specify a log level (0..4).\n"); +        return -1; +    } + +    if (pa_atou(m, &level) < 0 || level >= PA_LOG_LEVEL_MAX) { +        pa_strbuf_puts(buf, "Failed to parse log level.\n"); +        return -1; +    } + +    pa_log_set_maximal_level(level); + +    return 0; +} + +static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { +    const char *m; +    pa_bool_t b; + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    if (!(m = pa_tokenizer_get(t, 1))) { +        pa_strbuf_puts(buf, "You need to specify a boolean.\n"); +        return -1; +    } + +    if ((b = pa_parse_boolean(m)) < 0) { +        pa_strbuf_puts(buf, "Failed to parse log meta switch.\n"); +        return -1; +    } + +    pa_log_set_show_meta(b); + +    return 0; +} + +static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { +    const char *m; +    pa_bool_t b; + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    if (!(m = pa_tokenizer_get(t, 1))) { +        pa_strbuf_puts(buf, "You need to specify a boolean.\n"); +        return -1; +    } + +    if ((b = pa_parse_boolean(m)) < 0) { +        pa_strbuf_puts(buf, "Failed to parse log meta switch.\n"); +        return -1; +    } + +    pa_log_set_show_time(b); + +    return 0; +} + +static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { +    const char *m; +    uint32_t nframes; + +    pa_core_assert_ref(c); +    pa_assert(t); +    pa_assert(buf); +    pa_assert(fail); + +    if (!(m = pa_tokenizer_get(t, 1))) { +        pa_strbuf_puts(buf, "You need to specify a backtrace level.\n"); +        return -1; +    } + +    if (pa_atou(m, &nframes) < 0 || nframes >= 1000) { +        pa_strbuf_puts(buf, "Failed to parse backtrace level.\n"); +        return -1; +    } + +    pa_log_set_show_backtrace(nframes); + +    return 0; +} +  static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {      pa_module *m;      pa_sink *sink; @@ -1251,8 +1355,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b              nl = 1;          } -        pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink))); -        pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink))); +        pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, FALSE))); +        pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink, FALSE)));          pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));      } @@ -1265,8 +1369,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b              nl = 1;          } -        pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source))); -        pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source))); +        pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source, FALSE))); +        pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source, FALSE)));          pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED));      } diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index c92fca20..7bbc2660 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -149,10 +149,12 @@ char *pa_sink_list_to_string(pa_core *c) {              sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",              sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",              state_table[pa_sink_get_state(sink)], -            pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink)), -            pa_yes_no(pa_sink_get_mute(sink)), -            (double) pa_sink_get_latency(sink) / PA_USEC_PER_MSEC, -            (double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC, (double) min_latency / PA_USEC_PER_MSEC, (double) max_latency / PA_USEC_PER_MSEC, +            pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)), +            pa_yes_no(pa_sink_get_mute(sink, FALSE)), +            (double) pa_sink_get_latency(sink) / (double) PA_USEC_PER_MSEC, +            (double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC, +            (double) min_latency / PA_USEC_PER_MSEC, +            (double) max_latency / PA_USEC_PER_MSEC,              (unsigned long) pa_sink_get_max_request(sink) / 1024,              (unsigned long) pa_sink_get_max_rewind(sink) / 1024,              sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX, @@ -222,10 +224,12 @@ char *pa_source_list_to_string(pa_core *c) {              source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",              source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",              state_table[pa_source_get_state(source)], -            pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source)), -            pa_yes_no(pa_source_get_mute(source)), +            pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source, FALSE)), +            pa_yes_no(pa_source_get_mute(source, FALSE)),              (double) pa_source_get_latency(source) / PA_USEC_PER_MSEC, -            (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC, (double) min_latency / PA_USEC_PER_MSEC, (double) max_latency / PA_USEC_PER_MSEC, +            (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC, +            (double) min_latency / PA_USEC_PER_MSEC, +            (double) max_latency / PA_USEC_PER_MSEC,              (unsigned long) pa_source_get_max_rewind(source) / 1024,              pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),              pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map), @@ -411,7 +415,7 @@ char *pa_scache_list_to_string(pa_core *c) {              if (e->memchunk.memblock) {                  pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);                  pa_channel_map_snprint(cm, sizeof(cm), &e->channel_map); -                l = (double) e->memchunk.length / pa_bytes_per_second(&e->sample_spec); +                l = (double) e->memchunk.length / (double) pa_bytes_per_second(&e->sample_spec);              }              pa_strbuf_printf( diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c index 4aec45d7..ef6d6bb6 100644 --- a/src/pulsecore/conf-parser.c +++ b/src/pulsecore/conf-parser.c @@ -148,7 +148,7 @@ finish:      return r;  } -int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {      int *i = data;      int32_t k; @@ -166,7 +166,43 @@ int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue,      return 0;  } -int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +int pa_config_parse_unsigned(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) { +    unsigned *u = data; +    uint32_t k; + +    pa_assert(filename); +    pa_assert(lvalue); +    pa_assert(rvalue); +    pa_assert(data); + +    if (pa_atou(rvalue, &k) < 0) { +        pa_log("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); +        return -1; +    } + +    *u = (unsigned) k; +    return 0; +} + +int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) { +    size_t *i = data; +    uint32_t k; + +    pa_assert(filename); +    pa_assert(lvalue); +    pa_assert(rvalue); +    pa_assert(data); + +    if (pa_atou(rvalue, &k) < 0) { +        pa_log("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); +        return -1; +    } + +    *i = (size_t) k; +    return 0; +} + +int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {      int k;      pa_bool_t *b = data; @@ -185,7 +221,7 @@ int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue      return 0;  } -int pa_config_parse_string(const char *filename, PA_GCC_UNUSED unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {      char **s = data;      pa_assert(filename); diff --git a/src/pulsecore/conf-parser.h b/src/pulsecore/conf-parser.h index 7eb1fae2..48a0fd26 100644 --- a/src/pulsecore/conf-parser.h +++ b/src/pulsecore/conf-parser.h @@ -39,8 +39,10 @@ typedef struct pa_config_item {   * NULL */  int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata); -/* Generic parsers for integers, booleans and strings */ +/* Generic parsers for integers, size_t, booleans and strings */  int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); +int pa_config_parse_unsigned(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); +int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);  int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);  int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c index 75fa2ff1..1d080e11 100644 --- a/src/pulsecore/core-scache.c +++ b/src/pulsecore/core-scache.c @@ -63,7 +63,7 @@  #define UNLOAD_POLL_TIME 60 -static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { +static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) {      pa_core *c = userdata;      struct timeval ntv; @@ -98,7 +98,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {      pa_assert(c);      pa_assert(name); -    if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) { +    if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE))) {          if (e->memchunk.memblock)              pa_memblock_unref(e->memchunk.memblock); @@ -111,7 +111,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {      } else {          e = pa_xnew(pa_scache_entry, 1); -        if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, 1)) { +        if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, TRUE)) {              pa_xfree(e);              return NULL;          } @@ -134,9 +134,9 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {      e->lazy = FALSE;      e->last_used_time = 0; -    memset(&e->sample_spec, 0, sizeof(e->sample_spec)); +    pa_sample_spec_init(&e->sample_spec);      pa_channel_map_init(&e->channel_map); -    pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX); +    pa_cvolume_init(&e->volume);      pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event"); @@ -159,10 +159,12 @@ int pa_scache_add_item(      pa_assert(c);      pa_assert(name);      pa_assert(!ss || pa_sample_spec_valid(ss)); -    pa_assert(!map || (pa_channel_map_valid(map) && ss && ss->channels == map->channels)); +    pa_assert(!map || (pa_channel_map_valid(map) && ss && pa_channel_map_compatible(map, ss))); -    if (ss && !map) +    if (ss && !map) {          pa_channel_map_init_extend(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT); +        map = &tmap; +    }      if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX)          return -1; @@ -170,12 +172,13 @@ int pa_scache_add_item(      if (!(e = scache_add_item(c, name)))          return -1; -    memset(&e->sample_spec, 0, sizeof(e->sample_spec)); +    pa_sample_spec_init(&e->sample_spec);      pa_channel_map_init(&e->channel_map); +    pa_cvolume_init(&e->volume);      if (ss) {          e->sample_spec = *ss; -        e->volume.channels = e->sample_spec.channels; +        pa_cvolume_reset(&e->volume, ss->channels);      }      if (map) @@ -282,7 +285,7 @@ int pa_scache_remove_item(pa_core *c, const char *name) {      return 0;  } -static void free_cb(void *p, PA_GCC_UNUSED void *userdata) { +static void free_cb(void *p, void *userdata) {      pa_scache_entry *e = p;      pa_assert(e); @@ -310,17 +313,21 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t      pa_assert(name);      pa_assert(sink); -    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 1))) +    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE)))          return -1;      if (e->lazy && !e->memchunk.memblock) { +        pa_channel_map old_channel_map = e->channel_map; +          if (pa_sound_file_load(c->mempool, e->filename, &e->sample_spec, &e->channel_map, &e->memchunk) < 0)              return -1;          pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); -        if (e->volume.channels > e->sample_spec.channels) -            e->volume.channels = e->sample_spec.channels; +        if (pa_cvolume_valid(&e->volume)) +            pa_cvolume_remap(&e->volume, &old_channel_map, &e->channel_map); +        else +            pa_cvolume_reset(&e->volume, e->sample_spec.channels);      }      if (!e->memchunk.memblock) @@ -383,7 +390,7 @@ uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {      pa_assert(c);      pa_assert(name); -    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) +    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE)))          return PA_IDXSET_INVALID;      return e->index; diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c index 6107002b..c70d8adc 100644 --- a/src/pulsecore/core-subscribe.c +++ b/src/pulsecore/core-subscribe.c @@ -42,7 +42,7 @@  struct pa_subscription {      pa_core *core; -    int dead; +    pa_bool_t dead;      pa_subscription_cb_t callback;      void *userdata; @@ -72,7 +72,7 @@ pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_su      s = pa_xnew(pa_subscription, 1);      s->core = c; -    s->dead = 0; +    s->dead = FALSE;      s->callback = callback;      s->userdata = userdata;      s->mask = m; @@ -86,7 +86,7 @@ void pa_subscription_free(pa_subscription*s) {      pa_assert(s);      pa_assert(!s->dead); -    s->dead = 1; +    s->dead = TRUE;      sched_event(s->core);  } @@ -145,7 +145,7 @@ static void dump_event(const char * prefix, pa_subscription_event*e) {          [PA_SUBSCRIPTION_EVENT_REMOVE] = "REMOVE"      }; -    pa_log("%s event (%s|%s|%u)", +    pa_log_debug("%s event (%s|%s|%u)",             prefix,             fac_table[e->type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK],             type_table[e->type & PA_SUBSCRIPTION_EVENT_TYPE_MASK], @@ -234,7 +234,7 @@ void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t i                   * entry in the queue. */                  free_event(i); -                pa_log_debug("dropped redundant event."); +                pa_log_debug("Dropped redundant event due to remove event.");                  continue;              } @@ -242,7 +242,7 @@ void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t i                  /* This object has changed. If a "new" or "change" event for                   * this object is still in the queue we can exit. */ -                pa_log_debug("dropped redundant event."); +                pa_log_debug("Dropped redundant event due to change event.");                  return;              }          } diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index b2c91e45..dde34d7b 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -40,6 +40,9 @@  #include <sys/stat.h>  #include <sys/time.h>  #include <dirent.h> +#include <regex.h> +#include <langinfo.h> +#include <sys/utsname.h>  #ifdef HAVE_STRTOF_L  #include <locale.h> @@ -111,6 +114,8 @@ int pa_set_root(HANDLE handle) {      strcpy(library_path, PULSE_ROOTENV "="); +    /* FIXME: Needs to set errno */ +      if (!GetModuleFileName(handle, library_path + sizeof(PULSE_ROOTENV), MAX_PATH))          return 0; @@ -168,7 +173,7 @@ void pa_make_fd_cloexec(int fd) {  /** Creates a directory securely */  int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {      struct stat st; -    int r; +    int r, saved_errno;      pa_assert(dir); @@ -220,7 +225,10 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {      return 0;  fail: +    saved_errno = errno;      rmdir(dir); +    errno = saved_errno; +      return -1;  } @@ -230,6 +238,7 @@ char *pa_parent_dir(const char *fn) {      if ((slash = (char*) pa_path_get_filename(dir)) == dir) {          pa_xfree(dir); +        errno = ENOENT;          return NULL;      } @@ -337,7 +346,7 @@ ssize_t pa_loop_read(int fd, void*data, size_t size, int *type) {          ret += r;          data = (uint8_t*) data + r; -        size -= r; +        size -= (size_t) r;      }      return ret; @@ -368,7 +377,7 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) {          ret += r;          data = (const uint8_t*) data + r; -        size -= r; +        size -= (size_t) r;      }      return ret; @@ -390,7 +399,15 @@ int pa_close(int fd) {      }  #endif -    return close(fd); +    for (;;) { +        int r; + +        if ((r = close(fd)) >= 0) +            return r; + +        if (errno != EINTR) +            return r; +    }  }  /* Print a warning messages in case that the given signal is not @@ -437,7 +454,7 @@ void pa_check_signal_is_blocked(int sig) {  /* The following function is based on an example from the GNU libc   * documentation. This function is similar to GNU's asprintf(). */  char *pa_sprintf_malloc(const char *format, ...) { -    int  size = 100; +    size_t  size = 100;      char *c = NULL;      pa_assert(format); @@ -454,11 +471,11 @@ char *pa_sprintf_malloc(const char *format, ...) {          c[size-1] = 0; -        if (r > -1 && r < size) +        if (r > -1 && (size_t) r < size)              return c;          if (r > -1)    /* glibc 2.1 */ -            size = r+1; +            size = (size_t) r+1;          else           /* glibc 2.0 */              size *= 2;      } @@ -467,7 +484,7 @@ char *pa_sprintf_malloc(const char *format, ...) {  /* Same as the previous function, but use a va_list instead of an   * ellipsis */  char *pa_vsprintf_malloc(const char *format, va_list ap) { -    int  size = 100; +    size_t  size = 100;      char *c = NULL;      pa_assert(format); @@ -484,11 +501,11 @@ char *pa_vsprintf_malloc(const char *format, va_list ap) {          c[size-1] = 0; -        if (r > -1 && r < size) +        if (r > -1 && (size_t) r < size)              return c;          if (r > -1)    /* glibc 2.1 */ -            size = r+1; +            size = (size_t) r+1;          else           /* glibc 2.0 */              size *= 2;      } @@ -546,6 +563,8 @@ int pa_make_realtime(int rtprio) {      pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i.", sp.sched_priority);      return 0;  #else + +    errno = ENOTSUP;      return -1;  #endif  } @@ -653,6 +672,7 @@ int pa_raise_priority(int nice_level) {      if (nice_level < 0) {          if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {              pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError()); +            errno = EPERM;              return .-1;          } else              pa_log_info("Successfully gained high priority class."); @@ -679,15 +699,55 @@ void pa_reset_priority(void) {  #endif  } +static int match(const char *expr, const char *v) { +    int k; +    regex_t re; +    int r; + +    if (regcomp(&re, expr, REG_NOSUB|REG_EXTENDED) != 0) { +        errno = EINVAL; +        return -1; +    } + +    if ((k = regexec(&re, v, 0, NULL, 0)) == 0) +        r = 1; +    else if (k == REG_NOMATCH) +        r = 0; +    else +        r = -1; + +    regfree(&re); + +    if (r < 0) +        errno = EINVAL; + +    return r; +} +  /* Try to parse a boolean string value.*/  int pa_parse_boolean(const char *v) { +    const char *expr; +    int r;      pa_assert(v); +    /* First we check language independant */      if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))          return 1;      else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))          return 0; +    /* And then we check language dependant */ +    if ((expr = nl_langinfo(YESEXPR))) +        if (expr[0]) +            if ((r = match(expr, v)) > 0) +                return 1; + +    if ((expr = nl_langinfo(NOEXPR))) +        if (expr[0]) +            if ((r = match(expr, v)) > 0) +                return 0; + +    errno = EINVAL;      return -1;  } @@ -875,11 +935,18 @@ static int is_group(gid_t gid, const char *name) {  #else      n = -1;  #endif -    if (n < 0) n = 512; -    data = pa_xmalloc(n); +    if (n < 0) +        n = 512; + +    data = pa_xmalloc((size_t) n); + +    errno = 0; +    if (getgrgid_r(gid, &group, data, (size_t) n, &result) < 0 || !result) { +        pa_log("getgrgid_r(%u): %s", (unsigned) gid, pa_cstrerror(errno)); + +        if (!errno) +            errno = ENOENT; -    if (getgrgid_r(gid, &group, data, n, &result) < 0 || !result) { -        pa_log("getgrgid_r(%u): %s", (unsigned)gid, pa_cstrerror(errno));          goto finish;      } @@ -890,8 +957,14 @@ finish:  #else      /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not       * support getgrgid_r. */ + +    errno = 0;      if ((result = getgrgid(gid)) == NULL) {          pa_log("getgrgid(%u): %s", gid, pa_cstrerror(errno)); + +        if (!errno) +            errno = ENOENT; +          goto finish;      } @@ -906,27 +979,32 @@ finish:  /* Check the current user is member of the specified group */  int pa_own_uid_in_group(const char *name, gid_t *gid) {      GETGROUPS_T *gids, tgid; -    int n = sysconf(_SC_NGROUPS_MAX); -    int r = -1, i; +    long n = sysconf(_SC_NGROUPS_MAX); +    int r = -1, i, k;      pa_assert(n > 0); -    gids = pa_xmalloc(sizeof(GETGROUPS_T)*n); +    gids = pa_xmalloc(sizeof(GETGROUPS_T) * (size_t) n); -    if ((n = getgroups(n, gids)) < 0) { +    if ((n = getgroups((int) n, gids)) < 0) {          pa_log("getgroups(): %s", pa_cstrerror(errno));          goto finish;      }      for (i = 0; i < n; i++) { -        if (is_group(gids[i], name) > 0) { + +        if ((k = is_group(gids[i], name)) < 0) +            goto finish; +        else if (k > 0) {              *gid = gids[i];              r = 1;              goto finish;          }      } -    if (is_group(tgid = getgid(), name) > 0) { +    if ((k = is_group(tgid = getgid(), name)) < 0) +        goto finish; +    else if (k > 0) {          *gid = tgid;          r = 1;          goto finish; @@ -949,18 +1027,25 @@ int pa_uid_in_group(uid_t uid, const char *name) {      int r = -1;      g_n = sysconf(_SC_GETGR_R_SIZE_MAX); -    g_buf = pa_xmalloc(g_n); +    g_buf = pa_xmalloc((size_t) g_n);      p_n = sysconf(_SC_GETPW_R_SIZE_MAX); -    p_buf = pa_xmalloc(p_n); +    p_buf = pa_xmalloc((size_t) p_n); + +    errno = 0; +    if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) { + +        if (!errno) +            errno = ENOENT; -    if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)          goto finish; +    }      r = 0;      for (i = gr->gr_mem; *i; i++) {          struct passwd pwbuf, *pw; +        errno = 0;          if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw)              continue; @@ -985,10 +1070,16 @@ gid_t pa_get_gid_of_group(const char *name) {      struct group grbuf, *gr;      g_n = sysconf(_SC_GETGR_R_SIZE_MAX); -    g_buf = pa_xmalloc(g_n); +    g_buf = pa_xmalloc((size_t) g_n); + +    errno = 0; +    if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) { + +        if (!errno) +            errno = ENOENT; -    if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)          goto finish; +    }      ret = gr->gr_gid; @@ -1014,19 +1105,23 @@ int pa_check_in_group(gid_t g) {  #else /* HAVE_GRP_H */  int pa_own_uid_in_group(const char *name, gid_t *gid) { +    errno = ENOSUP;      return -1;  }  int pa_uid_in_group(uid_t uid, const char *name) { +    errno = ENOSUP;      return -1;  }  gid_t pa_get_gid_of_group(const char *name) { +    errno = ENOSUP;      return (gid_t) -1;  }  int pa_check_in_group(gid_t g) { +    errno = ENOSUP;      return -1;  } @@ -1040,7 +1135,7 @@ int pa_lock_fd(int fd, int b) {      /* Try a R/W lock first */ -    flock.l_type = b ? F_WRLCK : F_UNLCK; +    flock.l_type = (short) (b ? F_WRLCK : F_UNLCK);      flock.l_whence = SEEK_SET;      flock.l_start = 0;      flock.l_len = 0; @@ -1067,6 +1162,8 @@ int pa_lock_fd(int fd, int b) {          return 0;      pa_log("%slock failed: 0x%08X", !b ? "un" : "", GetLastError()); + +    /* FIXME: Needs to set errno! */  #endif      return -1; @@ -1133,8 +1230,11 @@ int pa_lock_lockfile(const char *fn) {  fail: -    if (fd >= 0) +    if (fd >= 0) { +        int saved_errno = errno;          pa_close(fd); +        errno = saved_errno; +    }      return -1;  } @@ -1180,6 +1280,7 @@ static char *get_pulse_home(void) {      if (st.st_uid != getuid()) {          pa_log_error("Home directory %s not ours.", h); +        errno = EACCES;          return NULL;      } @@ -1199,7 +1300,7 @@ char *pa_get_state_dir(void) {      /* If PULSE_STATE_PATH and PULSE_RUNTIME_PATH point to the same       * dir then this will break. */ -    if (pa_make_secure_dir(d, 0700, (pid_t) -1, (pid_t) -1) < 0)  { +    if (pa_make_secure_dir(d, 0700U, (uid_t) -1, (gid_t) -1) < 0)  {          pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));          pa_xfree(d);          return NULL; @@ -1214,31 +1315,43 @@ static char* make_random_dir(mode_t m) {          "ABCDEFGHIJKLMNOPQRSTUVWXYZ"          "0123456789"; -    char fn[24] = "/tmp/pulse-"; +    const char *tmpdir; +    char *fn; +    size_t pathlen; + +    if (!(tmpdir = getenv("TMPDIR"))) +        if (!(tmpdir = getenv("TMP"))) +            if (!(tmpdir = getenv("TEMP"))) +                tmpdir = getenv("TEMPDIR"); + +    if (!tmpdir || !pa_is_path_absolute(tmpdir)) +        tmpdir = "/tmp"; -    fn[sizeof(fn)-1] = 0; +    fn = pa_sprintf_malloc("%s/pulse-XXXXXXXXXXXX", tmpdir); +    pathlen = strlen(fn);      for (;;) { -        unsigned i; +        size_t i;          int r;          mode_t u;          int saved_errno; -        for (i = 11; i < sizeof(fn)-1; i++) +        for (i = pathlen - 12; i < pathlen; i++)              fn[i] = table[rand() % (sizeof(table)-1)];          u = umask((~m) & 0777);          r = mkdir(fn, m); +          saved_errno = errno;          umask(u); +        errno = saved_errno;          if (r >= 0) -            return pa_xstrdup(fn); - -        errno = saved_errno; +            return fn;          if (errno != EEXIST) {              pa_log_error("Failed to create random directory %s: %s", fn, pa_cstrerror(errno)); +            pa_xfree(fn);              return NULL;          }      } @@ -1269,6 +1382,7 @@ static int make_random_dir_and_link(mode_t m, const char *k) {  char *pa_get_runtime_dir(void) {      char *d, *k = NULL, *p = NULL, *t = NULL, *mid;      struct stat st; +    mode_t m;      /* The runtime directory shall contain dynamic data that needs NOT       * to be kept accross reboots and is usuallly private to the user, @@ -1277,12 +1391,11 @@ char *pa_get_runtime_dir(void) {       * this directory, we link it to a random subdir in /tmp, if it       * was not explicitly configured. */ -    if ((d = getenv("PULSE_RUNTIME_PATH"))) { -        mode_t m; +    m = pa_in_system_mode() ? 0755U : 0700U; -        m = pa_in_system_mode() ? 0755 : 0700; +    if ((d = getenv("PULSE_RUNTIME_PATH"))) { -        if (pa_make_secure_dir(d, m, (pid_t) -1, (pid_t) -1) < 0)  { +        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));              goto fail;          } @@ -1293,6 +1406,11 @@ char *pa_get_runtime_dir(void) {      if (!(d = get_pulse_home()))          goto fail; +    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)); +        goto fail; +    } +      if (!(mid = pa_machine_id())) {          pa_xfree(d);          goto fail; @@ -1332,6 +1450,7 @@ char *pa_get_runtime_dir(void) {          /* Make sure that this actually makes sense */          if (!pa_is_path_absolute(p)) {              pa_log_error("Path %s in link %s is not absolute.", p, k); +            errno = ENOENT;              goto fail;          } @@ -1423,6 +1542,7 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env  #ifdef OS_IS_WIN32          if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX)) +            /* FIXME: Needs to set errno! */              return NULL;          fn = buf;  #endif @@ -1453,6 +1573,7 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env  #ifdef OS_IS_WIN32          if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) { +            /* FIXME: Needs to set errno! */              pa_xfree(lfn);              return NULL;          } @@ -1481,6 +1602,7 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env  #ifdef OS_IS_WIN32          if (!ExpandEnvironmentStrings(global, buf, PATH_MAX)) +            /* FIXME: Needs to set errno! */              return NULL;          global = buf;  #endif @@ -1492,9 +1614,9 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env              return f;          } -    } else -        errno = ENOENT; +    } +    errno = ENOENT;      return NULL;  } @@ -1511,6 +1633,7 @@ char *pa_find_config_file(const char *global, const char *local, const char *env  #ifdef OS_IS_WIN32          if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX)) +            /* FIXME: Needs to set errno! */              return NULL;          fn = buf;  #endif @@ -1536,6 +1659,7 @@ char *pa_find_config_file(const char *global, const char *local, const char *env  #ifdef OS_IS_WIN32          if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) { +            /* FIXME: Needs to set errno! */              pa_xfree(lfn);              return NULL;          } @@ -1560,14 +1684,16 @@ char *pa_find_config_file(const char *global, const char *local, const char *env      if (global) {  #ifdef OS_IS_WIN32          if (!ExpandEnvironmentStrings(global, buf, PATH_MAX)) +            /* FIXME: Needs to set errno! */              return NULL;          global = buf;  #endif          if (access(global, R_OK) == 0)              return pa_xstrdup(global); -    } else -        errno = ENOENT; +    } + +    errno = ENOENT;      return NULL;  } @@ -1604,6 +1730,7 @@ static int hexc(char c) {      if (c >= 'a' && c <= 'f')          return c - 'a' + 10; +    errno = EINVAL;      return -1;  } @@ -1742,11 +1869,16 @@ int pa_atoi(const char *s, int32_t *ret_i) {      errno = 0;      l = strtol(s, &x, 0); -    if (!x || *x || errno != 0) +    if (!x || *x || errno) { +        if (!errno) +            errno = EINVAL;          return -1; +    } -    if ((int32_t) l != l) +    if ((int32_t) l != l) { +        errno = ERANGE;          return -1; +    }      *ret_i = (int32_t) l; @@ -1764,11 +1896,16 @@ int pa_atou(const char *s, uint32_t *ret_u) {      errno = 0;      l = strtoul(s, &x, 0); -    if (!x || *x || errno != 0) +    if (!x || *x || errno) { +        if (!errno) +            errno = EINVAL;          return -1; +    } -    if ((uint32_t) l != l) +    if ((uint32_t) l != l) { +        errno = ERANGE;          return -1; +    }      *ret_u = (uint32_t) l; @@ -1786,7 +1923,6 @@ static void c_locale_destroy(void) {  int pa_atod(const char *s, double *ret_d) {      char *x = NULL;      double f; -    int r = 0;      pa_assert(s);      pa_assert(ret_d); @@ -1812,17 +1948,20 @@ int pa_atod(const char *s, double *ret_d) {          f = strtod(s, &x);      } -    if (!x || *x || errno != 0) -        r =  -1; -    else -        *ret_d = f; +    if (!x || *x || errno) { +        if (!errno) +            errno = EINVAL; +        return -1; +    } -    return r; +    *ret_d = f; + +    return 0;  }  /* Same as snprintf, but guarantees NUL-termination on every platform */ -int pa_snprintf(char *str, size_t size, const char *format, ...) { -    int ret; +size_t pa_snprintf(char *str, size_t size, const char *format, ...) { +    size_t ret;      va_list ap;      pa_assert(str); @@ -1837,7 +1976,7 @@ int pa_snprintf(char *str, size_t size, const char *format, ...) {  }  /* Same as vsnprintf, but guarantees NUL-termination on every platform */ -int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap) { +size_t pa_vsnprintf(char *str, size_t size, const char *format, va_list ap) {      int ret;      pa_assert(str); @@ -1849,9 +1988,12 @@ int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap) {      str[size-1] = 0;      if (ret < 0) -        ret = strlen(str); +        return strlen(str); -    return PA_MIN((int) size-1, ret); +    if ((size_t) ret > size-1) +        return size-1; + +    return (size_t) ret;  }  /* Truncate the specified string, but guarantee that the string @@ -1875,7 +2017,7 @@ char *pa_getcwd(void) {      size_t l = 128;      for (;;) { -        char *p = pa_xnew(char, l); +        char *p = pa_xmalloc(l);          if (getcwd(p, l))              return p; @@ -1900,7 +2042,7 @@ void *pa_will_need(const void *p, size_t l) {      pa_assert(l > 0);      a = PA_PAGE_ALIGN_PTR(p); -    size = (const uint8_t*) p + l - (const uint8_t*) a; +    size = (size_t) ((const uint8_t*) p + l - (const uint8_t*) a);  #ifdef HAVE_POSIX_MADVISE      if ((r = posix_madvise((void*) a, size, POSIX_MADV_WILLNEED)) == 0) { @@ -1921,10 +2063,11 @@ void *pa_will_need(const void *p, size_t l) {      if (rlim.rlim_cur < PA_PAGE_SIZE) {          pa_log_debug("posix_madvise() failed (or doesn't exist), resource limits don't allow mlock(), can't page in data: %s", pa_cstrerror(r)); +        errno = EPERM;          return (void*) p;      } -    bs = PA_PAGE_ALIGN(rlim.rlim_cur); +    bs = PA_PAGE_ALIGN((size_t) rlim.rlim_cur);  #else      bs = PA_PAGE_SIZE*4;  #endif @@ -1976,7 +2119,7 @@ char *pa_readlink(const char *p) {          char *c;          ssize_t n; -        c = pa_xnew(char, l); +        c = pa_xmalloc(l);          if ((n = readlink(p, c, l-1)) < 0) {              pa_xfree(c); @@ -1995,8 +2138,8 @@ char *pa_readlink(const char *p) {  int pa_close_all(int except_fd, ...) {      va_list ap; -    int n = 0, i, r; -    int *p; +    unsigned n = 0, i; +    int r, *p;      va_start(ap, except_fd); @@ -2123,8 +2266,8 @@ int pa_close_allv(const int except_fds[]) {  int pa_unblock_sigs(int except, ...) {      va_list ap; -    int n = 0, i, r; -    int *p; +    unsigned n = 0, i; +    int r, *p;      va_start(ap, except); @@ -2172,8 +2315,8 @@ int pa_unblock_sigsv(const int except[]) {  int pa_reset_sigs(int except, ...) {      va_list ap; -    int n = 0, i, r; -    int *p; +    unsigned n = 0, i; +    int *p, r;      va_start(ap, except); @@ -2208,7 +2351,7 @@ int pa_reset_sigs(int except, ...) {  int pa_reset_sigsv(const int except[]) {      int sig; -    for (sig = 1; sig < _NSIG; sig++) { +    for (sig = 1; sig < NSIG; sig++) {          pa_bool_t reset = TRUE;          switch (sig) { @@ -2266,36 +2409,48 @@ char *pa_machine_id(void) {      FILE *f;      size_t l; +    /* The returned value is supposed be some kind of ascii identifier +     * that is unique and stable across reboots. */ + +    /* First we try the D-Bus UUID, which is the best option we have, +     * since it fits perfectly our needs and is not as volatile as the +     * hostname which might be set from dhcp. */ +      if ((f = fopen(PA_MACHINE_ID, "r"))) {          char ln[34] = "", *r;          r = fgets(ln, sizeof(ln)-1, f);          fclose(f); -        if (r) -            return pa_xstrdup(pa_strip_nl(ln)); +        pa_strip_nl(ln); + +        if (ln[0]) +            return pa_xstrdup(ln);      } -    l = 100; +    /* The we fall back to the host name. It supposed to be somewhat +     * unique, at least in a network, but may change. */ +    l = 100;      for (;;) {          char *c; -        c = pa_xnew(char, l); +        c = pa_xmalloc(l);          if (!pa_get_host_name(c, l)) { -            if (errno == EINVAL || errno == ENAMETOOLONG) { +            if (errno != EINVAL && errno != ENAMETOOLONG) +                break; + +        } else if (strlen(c) < l-1) { + +            if (*c == 0) {                  pa_xfree(c); -                l *= 2; -                continue; +                break;              } -            return NULL; -        } - -        if (strlen(c) < l-1)              return c; +        }          /* Hmm, the hostname is as long the space we offered the           * function, we cannot know if it fully fit in, so let's play @@ -2304,4 +2459,31 @@ char *pa_machine_id(void) {          pa_xfree(c);          l *= 2;      } + +    /* If no hostname was set we use the POSIX hostid. It's usually +     * the IPv4 address.  Mit not be that stable. */ +    return pa_sprintf_malloc("%08lx", (unsigned long) gethostid); +} + +char *pa_uname_string(void) { +    struct utsname u; + +    pa_assert_se(uname(&u) == 0); + +    return pa_sprintf_malloc("%s %s %s %s", u.sysname, u.machine, u.release, u.version);  } + +#ifdef HAVE_VALGRIND_MEMCHECK_H +pa_bool_t pa_in_valgrind(void) { +    static int b = 0; + +    /* To make heisenbugs a bit simpler to find we check for $VALGRIND +     * here instead of really checking whether we run in valgrind or +     * not. */ + +    if (b < 1) +        b = getenv("VALGRIND") ? 2 : 1; + +    return b > 1; +} +#endif diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index 838e4ad3..fd6ee896 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -35,6 +35,10 @@  #include <pulse/gccmacro.h>  #include <pulsecore/macro.h> +#ifndef PACKAGE +#error "Please include config.h before including this file!" +#endif +  struct timeval;  /* These resource limits are pretty new on Linux, let's define them @@ -127,8 +131,8 @@ int pa_atoi(const char *s, int32_t *ret_i);  int pa_atou(const char *s, uint32_t *ret_u);  int pa_atod(const char *s, double *ret_d); -int pa_snprintf(char *str, size_t size, const char *format, ...); -int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap); +size_t pa_snprintf(char *str, size_t size, const char *format, ...); +size_t pa_vsnprintf(char *str, size_t size, const char *format, va_list ap);  char *pa_truncate_utf8(char *c, size_t l); @@ -142,29 +146,35 @@ static inline int pa_is_power_of_two(unsigned n) {      return !(n & (n - 1));  } -static inline unsigned pa_make_power_of_two(unsigned n) { -    unsigned j = n; +static inline unsigned pa_ulog2(unsigned n) { -    if (pa_is_power_of_two(n)) -        return n; +    if (n <= 1) +        return 0; -    while (j) { -        j = j >> 1; -        n = n | j; -    } +#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +    return 8U * (unsigned) sizeof(unsigned) - (unsigned) __builtin_clz(n) - 1; +#else +{ +    unsigned r = 0; -    return n + 1; -} +    for (;;) { +        n = n >> 1; -static inline unsigned pa_ulog2(unsigned n) { -    unsigned r = 0; +        if (!n) +            return r; -    while (n) {          r++; -        n = n >> 1;      } +} +#endif +} + +static inline unsigned pa_make_power_of_two(unsigned n) { + +    if (pa_is_power_of_two(n)) +        return n; -    return r; +    return 1U << (pa_ulog2(n) + 1);  }  void pa_close_pipe(int fds[2]); @@ -185,5 +195,15 @@ pa_bool_t pa_in_system_mode(void);  #define pa_streq(a,b) (!strcmp((a),(b)))  char *pa_machine_id(void); +char *pa_uname_string(void); + + +#ifdef HAVE_VALGRIND_MEMCHECK_H +pa_bool_t pa_in_valgrind(void); +#else +static inline pa_bool_t pa_in_valgrind(void) { +    return FALSE; +} +#endif  #endif diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index 5c594b02..5761bbc7 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -66,7 +66,7 @@ static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t o  static void core_free(pa_object *o); -pa_core* pa_core_new(pa_mainloop_api *m, int shared) { +pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {      pa_core* c;      pa_mempool *pool;      int j; @@ -74,14 +74,14 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {      pa_assert(m);      if (shared) { -        if (!(pool = pa_mempool_new(shared))) { +        if (!(pool = pa_mempool_new(shared, shm_size))) {              pa_log_warn("failed to allocate shared memory pool. Falling back to a normal memory pool."); -            shared = 0; +            shared = FALSE;          }      }      if (!shared) { -        if (!(pool = pa_mempool_new(shared))) { +        if (!(pool = pa_mempool_new(shared, shm_size))) {              pa_log("pa_mempool_new() failed.");              return NULL;          } @@ -138,6 +138,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {      c->realtime_scheduling = FALSE;      c->realtime_priority = 5;      c->disable_remixing = FALSE; +    c->disable_lfe_remixing = FALSE;      for (j = 0; j < PA_CORE_HOOK_MAX; j++)          pa_hook_init(&c->hooks[j], c); @@ -200,10 +201,11 @@ static void core_free(pa_object *o) {      pa_xfree(c);  } -static void exit_callback(pa_mainloop_api*m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { +static void exit_callback(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {      pa_core *c = userdata;      pa_assert(c->exit_event == e); +    pa_log_info("We are idle, quitting...");      pa_core_exit(c, TRUE, 0);  } diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index eb768418..f796fb93 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -49,6 +49,7 @@ typedef enum pa_core_hook {      PA_CORE_HOOK_SINK_UNLINK_POST,      PA_CORE_HOOK_SINK_STATE_CHANGED,      PA_CORE_HOOK_SINK_PROPLIST_CHANGED, +    PA_CORE_HOOK_SINK_SET_VOLUME,      PA_CORE_HOOK_SOURCE_NEW,      PA_CORE_HOOK_SOURCE_FIXATE,      PA_CORE_HOOK_SOURCE_PUT, @@ -65,6 +66,7 @@ typedef enum pa_core_hook {      PA_CORE_HOOK_SINK_INPUT_MOVE_POST,      PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,      PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED, +    PA_CORE_HOOK_SINK_INPUT_SET_VOLUME,      PA_CORE_HOOK_SOURCE_OUTPUT_NEW,      PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,      PA_CORE_HOOK_SOURCE_OUTPUT_PUT, @@ -124,6 +126,7 @@ struct pa_core {      pa_bool_t running_as_daemon:1;      pa_bool_t realtime_scheduling:1;      pa_bool_t disable_remixing:1; +    pa_bool_t disable_lfe_remixing:1;      pa_resample_method_t resample_method;      int realtime_priority; @@ -140,7 +143,7 @@ enum {      PA_CORE_MESSAGE_MAX  }; -pa_core* pa_core_new(pa_mainloop_api *m, int shared); +pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size);  /* Check whether noone is connected to this core */  void pa_core_check_idle(pa_core *c); diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h index 26336918..1b94de17 100644 --- a/src/pulsecore/endianmacros.h +++ b/src/pulsecore/endianmacros.h @@ -46,9 +46,14 @@  #endif  static inline float PA_FLOAT32_SWAP(float x) { -    uint32_t i = *(uint32_t*) &x; -    i = PA_UINT32_SWAP(i); -    return *(float*) &i; +    union { +        float f; +        uint32_t u; +    } t; + +    t.f = x; +    t.u = PA_UINT32_SWAP(t.u); +    return t.f;  }  #define PA_MAYBE_INT16_SWAP(c,x) ((c) ? PA_INT32_SWAP(x) : x) diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c index e2691611..7f2252e9 100644 --- a/src/pulsecore/envelope.c +++ b/src/pulsecore/envelope.c @@ -153,11 +153,11 @@ void pa_envelope_free(pa_envelope *e) {  }  static int32_t linear_interpolate_int(pa_usec_t x1, int32_t _y1, pa_usec_t x2, int32_t y2, pa_usec_t x3) { -    return (int32_t) (_y1 + (x3 - x1) * (float) (y2 - _y1) / (float) (x2 - x1)); +    return (int32_t) ((double) _y1 + (double) (x3 - x1) * (double) (y2 - _y1) / (double) (x2 - x1));  }  static float linear_interpolate_float(pa_usec_t x1, float _y1, pa_usec_t x2, float y2, pa_usec_t x3) { -    return _y1 + (x3 - x1) * (y2 - _y1) / (x2 - x1); +    return _y1 + ((float) x3 - (float) x1) * (y2 - _y1) / ((float) x2 - (float) x1);  }  static int32_t item_get_int(pa_envelope_item *i, pa_usec_t x) { @@ -573,11 +573,11 @@ static float linear_get_float(pa_envelope *e, int v) {      if (!e->points[v].cached_valid) {          e->points[v].cached_dy_dx =              (e->points[v].y.f[e->points[v].n_current+1] - e->points[v].y.f[e->points[v].n_current]) / -            (e->points[v].x[e->points[v].n_current+1] - e->points[v].x[e->points[v].n_current]); +            ((float) e->points[v].x[e->points[v].n_current+1] - (float) e->points[v].x[e->points[v].n_current]);          e->points[v].cached_valid = TRUE;      } -    return e->points[v].y.f[e->points[v].n_current] + (e->x - e->points[v].x[e->points[v].n_current]) * e->points[v].cached_dy_dx; +    return e->points[v].y.f[e->points[v].n_current] + (float) (e->x - e->points[v].x[e->points[v].n_current]) * e->points[v].cached_dy_dx;  }  void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) { @@ -605,7 +605,7 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {                  uint8_t *t;                  for (t = p; n > 0; n -= fs) { -                    int16_t factor = linear_get_int(e, v); +                    int32_t factor = linear_get_int(e, v);                      unsigned c;                      e->x += fs; @@ -620,13 +620,13 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {                  uint8_t *t;                  for (t = p; n > 0; n -= fs) { -                    int16_t factor = linear_get_int(e, v); +                    int32_t factor = linear_get_int(e, v);                      unsigned c;                      e->x += fs;                      for (c = 0; c < e->sample_spec.channels; c++, t++) {                          int16_t k = st_ulaw2linear16(*t); -                        *t = (uint8_t) st_14linear2ulaw(((factor * k) / 0x10000) >> 2); +                        *t = (uint8_t) st_14linear2ulaw((int16_t) (((factor * k) / 0x10000) >> 2));                      }                  } @@ -637,13 +637,13 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {                  uint8_t *t;                  for (t = p; n > 0; n -= fs) { -                    int16_t factor = linear_get_int(e, v); +                    int32_t factor = linear_get_int(e, v);                      unsigned c;                      e->x += fs;                      for (c = 0; c < e->sample_spec.channels; c++, t++) {                          int16_t k = st_alaw2linear16(*t); -                        *t = (uint8_t) st_13linear2alaw(((factor * k) / 0x10000) >> 3); +                        *t = (uint8_t) st_13linear2alaw((int16_t) (((factor * k) / 0x10000) >> 3));                      }                  } @@ -659,7 +659,7 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {                      e->x += fs;                      for (c = 0; c < e->sample_spec.channels; c++, t++) -                        *t = (factor * *t) / 0x10000; +                        *t = (int16_t) ((factor * *t) / 0x10000);                  }                  break; @@ -674,7 +674,7 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {                      e->x += fs;                      for (c = 0; c < e->sample_spec.channels; c++, t++) { -                        int16_t r = (factor * PA_INT16_SWAP(*t)) / 0x10000; +                        int16_t r = (int16_t) ((factor * PA_INT16_SWAP(*t)) / 0x10000);                          *t = PA_INT16_SWAP(r);                      }                  } diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c index 1531e3db..380f34f5 100644 --- a/src/pulsecore/fdsem.c +++ b/src/pulsecore/fdsem.c @@ -41,39 +41,15 @@  #include <pulsecore/pipe.h>  #endif -#ifdef __linux__ - -#if !defined(__NR_eventfd) && defined(__i386__) -#define __NR_eventfd 323 -#endif - -#if !defined(__NR_eventfd) && defined(__x86_64__) -#define __NR_eventfd 284 -#endif - -#if !defined(__NR_eventfd) && defined(__arm__) -#define __NR_eventfd (__NR_SYSCALL_BASE+351) -#endif - -#if !defined(SYS_eventfd) && defined(__NR_eventfd) -#define SYS_eventfd __NR_eventfd -#endif - -#ifdef SYS_eventfd -#define HAVE_EVENTFD - -static inline long eventfd(unsigned count) { -    return syscall(SYS_eventfd, count); -} - -#endif +#ifdef HAVE_SYS_EVENTFD_H +#include <sys/eventfd.h>  #endif  #include "fdsem.h"  struct pa_fdsem {      int fds[2]; -#ifdef HAVE_EVENTFD +#ifdef HAVE_SYS_EVENTFD_H      int efd;  #endif @@ -85,8 +61,8 @@ pa_fdsem *pa_fdsem_new(void) {      f = pa_xmalloc(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data))); -#ifdef HAVE_EVENTFD -    if ((f->efd = eventfd(0)) >= 0) { +#ifdef HAVE_SYS_EVENTFD_H +    if ((f->efd = eventfd(0, 0)) >= 0) {          pa_make_fd_cloexec(f->efd);          f->fds[0] = f->fds[1] = -1;      } else @@ -116,7 +92,7 @@ pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd) {      pa_assert(data);      pa_assert(event_fd >= 0); -#ifdef HAVE_EVENTFD +#ifdef HAVE_SYS_EVENTFD_H      f = pa_xnew(pa_fdsem, 1);      f->efd = event_fd; @@ -134,11 +110,11 @@ pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd) {      pa_assert(data);      pa_assert(event_fd); -#ifdef HAVE_EVENTFD +#ifdef HAVE_SYS_EVENTFD_H      f = pa_xnew(pa_fdsem, 1); -    if ((f->efd = eventfd(0)) < 0) { +    if ((f->efd = eventfd(0, 0)) < 0) {          pa_xfree(f);          return NULL;      } @@ -159,7 +135,7 @@ pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd) {  void pa_fdsem_free(pa_fdsem *f) {      pa_assert(f); -#ifdef HAVE_EVENTFD +#ifdef HAVE_SYS_EVENTFD_H      if (f->efd >= 0)          pa_close(f->efd);  #endif @@ -178,7 +154,7 @@ static void flush(pa_fdsem *f) {      do {          char x[10]; -#ifdef HAVE_EVENTFD +#ifdef HAVE_SYS_EVENTFD_H          if (f->efd >= 0) {              uint64_t u; @@ -195,7 +171,7 @@ static void flush(pa_fdsem *f) {              continue;          } -    } while (pa_atomic_sub(&f->data->in_pipe, r) > r); +    } while (pa_atomic_sub(&f->data->in_pipe, (int) r) > (int) r);  }  void pa_fdsem_post(pa_fdsem *f) { @@ -211,7 +187,7 @@ void pa_fdsem_post(pa_fdsem *f) {              for (;;) { -#ifdef HAVE_EVENTFD +#ifdef HAVE_SYS_EVENTFD_H                  if (f->efd >= 0) {                      uint64_t u = 1; @@ -247,7 +223,7 @@ void pa_fdsem_wait(pa_fdsem *f) {          char x[10];          ssize_t r; -#ifdef HAVE_EVENTFD +#ifdef HAVE_SYS_EVENTFD_H          if (f->efd >= 0) {              uint64_t u; @@ -265,7 +241,7 @@ void pa_fdsem_wait(pa_fdsem *f) {              continue;          } -        pa_atomic_sub(&f->data->in_pipe, r); +        pa_atomic_sub(&f->data->in_pipe, (int) r);      }      pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1); @@ -285,7 +261,7 @@ int pa_fdsem_try(pa_fdsem *f) {  int pa_fdsem_get(pa_fdsem *f) {      pa_assert(f); -#ifdef HAVE_EVENTFD +#ifdef HAVE_SYS_EVENTFD_H      if (f->efd >= 0)          return f->efd;  #endif diff --git a/src/pulsecore/flist.h b/src/pulsecore/flist.h index 2d8422f9..512dd357 100644 --- a/src/pulsecore/flist.h +++ b/src/pulsecore/flist.h @@ -26,6 +26,7 @@  #include <pulse/gccmacro.h>  #include <pulsecore/once.h> +#include <pulsecore/core-util.h>  /* A multiple-reader multipler-write lock-free free list implementation */ @@ -56,6 +57,8 @@ void* pa_flist_pop(pa_flist*l);      }                                                                   \      static void name##_flist_destructor(void) PA_GCC_DESTRUCTOR;        \      static void name##_flist_destructor(void) {                         \ +        if (!pa_in_valgrind())                                          \ +            return;                                                     \          if (name##_flist.flist)                                         \              pa_flist_free(name##_flist.flist, (free_cb));               \      }                                                                   \ diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c index fb4497b8..24a28db7 100644 --- a/src/pulsecore/idxset.c +++ b/src/pulsecore/idxset.c @@ -66,7 +66,7 @@ unsigned pa_idxset_string_hash_func(const void *p) {      const char *c;      for (c = p; *c; c++) -        hash = 31 * hash + *c; +        hash = 31 * hash + (unsigned) *c;      return hash;  } @@ -80,7 +80,7 @@ unsigned pa_idxset_trivial_hash_func(const void *p) {  }  int pa_idxset_trivial_compare_func(const void *a, const void *b) { -    return a != b; +    return a < b ? -1 : (a > b ? 1 : 0);  }  pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) { @@ -318,8 +318,7 @@ void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) {      hash = *idx % NBUCKETS; -    if (!(e = index_scan(s, hash, *idx))) -        return NULL; +    e = index_scan(s, hash, *idx);      if (e && e->iterate_next)          e = e->iterate_next; diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c index 90afaafd..88174c05 100644 --- a/src/pulsecore/ioline.c +++ b/src/pulsecore/ioline.c @@ -164,7 +164,7 @@ void pa_ioline_puts(pa_ioline *l, const char *c) {          /* In case the allocated buffer is too small, enlarge it. */          if (l->wbuf_valid_length + len > l->wbuf_length) {              size_t n = l->wbuf_valid_length+len; -            char *new = pa_xnew(char, n); +            char *new = pa_xnew(char, (unsigned) n);              if (l->wbuf) {                  memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length); @@ -285,7 +285,7 @@ static int do_read(pa_ioline *l) {                      memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length);              } else {                  /* Enlarge the buffer */ -                char *new = pa_xnew(char, n); +                char *new = pa_xnew(char, (unsigned) n);                  if (l->rbuf_valid_length)                      memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);                  pa_xfree(l->rbuf); @@ -315,10 +315,10 @@ static int do_read(pa_ioline *l) {              return -1;          } -        l->rbuf_valid_length += r; +        l->rbuf_valid_length += (size_t) r;          /* Look if a line has been terminated in the newly read data */ -        scan_for_lines(l, l->rbuf_valid_length - r); +        scan_for_lines(l, l->rbuf_valid_length - (size_t) r);      }      return 0; @@ -346,8 +346,8 @@ static int do_write(pa_ioline *l) {              return -1;          } -        l->wbuf_index += r; -        l->wbuf_valid_length -= r; +        l->wbuf_index += (size_t) r; +        l->wbuf_valid_length -= (size_t) r;          /* A shortcut for the next time */          if (l->wbuf_valid_length == 0) diff --git a/src/pulsecore/ipacl.c b/src/pulsecore/ipacl.c index 7b5f865b..6d5080fb 100644 --- a/src/pulsecore/ipacl.c +++ b/src/pulsecore/ipacl.c @@ -122,7 +122,7 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {              if (e.bits < 128) {                  int t = 0, i; -                for (i = 0, bits = e.bits; i < 16; i++) { +                for (i = 0, bits = (uint32_t) e.bits; i < 16; i++) {                      if (bits >= 8)                          bits -= 8; diff --git a/src/pulsecore/lock-autospawn.c b/src/pulsecore/lock-autospawn.c new file mode 100644 index 00000000..d36b669e --- /dev/null +++ b/src/pulsecore/lock-autospawn.c @@ -0,0 +1,330 @@ +/*** +  This file is part of PulseAudio. + +  Copyright 2008 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 <fcntl.h> +#include <errno.h> +#include <string.h> +#include <sys/poll.h> +#include <signal.h> +#include <pthread.h> + +#include <pulse/i18n.h> +#include <pulse/xmalloc.h> + +#include <pulsecore/mutex.h> +#include <pulsecore/thread.h> +#include <pulsecore/core-util.h> + +#include "lock-autospawn.h" + +/* So, why do we have this complex code here with threads and pipes + * and stuff? For two reasons: POSIX file locks are per-process, not + * per-file descriptor. That means that two contexts within the same + * process that try to create the autospawn lock might end up assuming + * they both managed to lock the file. And then, POSIX locking + * operations are synchronous. If two contexts run from the same event + * loop it must be made sure that they do not block each other, but + * that the locking operation can happen asynchronously. */ + +#define AUTOSPAWN_LOCK "autospawn.lock" + +static pa_mutex *mutex; + +static unsigned n_ref = 0; +static int lock_fd = -1; +static pa_mutex *lock_fd_mutex = NULL; +static pa_bool_t taken = FALSE; +static pa_thread *thread; +static int pipe_fd[2] = { -1, -1 }; + +static void destroy_mutex(void) PA_GCC_DESTRUCTOR; + +static int ref(void) { + +    if (n_ref > 0) { + +        pa_assert(pipe_fd[0] >= 0); +        pa_assert(pipe_fd[1] >= 0); + +        n_ref++; + +        return 0; +    } + +    pa_assert(lock_fd < 0); +    pa_assert(!lock_fd_mutex); +    pa_assert(!taken); +    pa_assert(!thread); +    pa_assert(pipe_fd[0] < 0); +    pa_assert(pipe_fd[1] < 0); + +    if (pipe(pipe_fd) < 0) +        return -1; + +    lock_fd_mutex = pa_mutex_new(FALSE, FALSE); + +    pa_make_fd_cloexec(pipe_fd[0]); +    pa_make_fd_cloexec(pipe_fd[1]); + +    pa_make_fd_nonblock(pipe_fd[1]); +    pa_make_fd_nonblock(pipe_fd[0]); + +    n_ref = 1; +    return 0; +} + +static void unref(pa_bool_t after_fork) { + +    pa_assert(n_ref > 0); +    pa_assert(pipe_fd[0] >= 0); +    pa_assert(pipe_fd[1] >= 0); +    pa_assert(lock_fd_mutex); + +    n_ref--; + +    if (n_ref > 0) +        return; + +    pa_assert(!taken); + +    if (thread) { +        pa_thread_free(thread); +        thread = NULL; +    } + +    pa_mutex_lock(lock_fd_mutex); +    if (lock_fd >= 0) { + +        if (after_fork) +            pa_close(lock_fd); +        else { +            char *lf; + +            if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) +                pa_log_warn(_("Cannot access autospawn lock.")); + +            pa_unlock_lockfile(lf, lock_fd); +            pa_xfree(lf); + +            lock_fd = -1; +        } +    } +    pa_mutex_unlock(lock_fd_mutex); + +    pa_mutex_free(lock_fd_mutex); +    lock_fd_mutex = NULL; + +    pa_close(pipe_fd[0]); +    pa_close(pipe_fd[1]); +    pipe_fd[0] = pipe_fd[1] = -1; +} + +static void ping(void) { +    ssize_t s; + +    pa_assert(pipe_fd[1] >= 0); + +    for (;;) { +        char x = 'x'; + +        if ((s = write(pipe_fd[1], &x, 1)) == 1) +            break; + +        pa_assert(s < 0); + +        if (errno == EAGAIN) +            break; + +        pa_assert(errno == EINTR); +    } +} + +static void wait_for_ping(void) { +    ssize_t s; +    char x; +    struct pollfd pfd; +    int k; + +    pa_assert(pipe_fd[0] >= 0); + +    memset(&pfd, 0, sizeof(pfd)); +    pfd.fd = pipe_fd[0]; +    pfd.events = POLLIN; + +    if ((k = poll(&pfd, 1, -1)) != 1) { +        pa_assert(k < 0); +        pa_assert(errno == EINTR); +    } else if ((s = read(pipe_fd[0], &x, 1)) != 1) { +        pa_assert(s < 0); +        pa_assert(errno == EAGAIN); +    } +} + +static void empty_pipe(void) { +    char x[16]; +    ssize_t s; + +    pa_assert(pipe_fd[0] >= 0); + +    if ((s = read(pipe_fd[0], &x, sizeof(x))) < 1) { +        pa_assert(s < 0); +        pa_assert(errno == EAGAIN); +    } +} + +static void thread_func(void *u) { +    int fd; +    char *lf; +    sigset_t fullset; + +    /* No signals in this thread please */ +    sigfillset(&fullset); +    pthread_sigmask(SIG_BLOCK, &fullset, NULL); + +    if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) { +        pa_log_warn(_("Cannot access autospawn lock.")); +        goto finish; +    } + +    if ((fd = pa_lock_lockfile(lf)) < 0) +        goto finish; + +    pa_mutex_lock(lock_fd_mutex); +    pa_assert(lock_fd < 0); +    lock_fd = fd; +    pa_mutex_unlock(lock_fd_mutex); + +finish: +    pa_xfree(lf); + +    ping(); +} + +static int start_thread(void) { + +    if (!thread) +        if (!(thread = pa_thread_new(thread_func, NULL))) +            return -1; + +    return 0; +} + +static void create_mutex(void) { +    PA_ONCE_BEGIN { +        mutex = pa_mutex_new(FALSE, FALSE); +    } PA_ONCE_END; +} + +static void destroy_mutex(void) { + +    if (mutex) +        pa_mutex_free(mutex); +} + + +int pa_autospawn_lock_init(void) { +    int ret = -1; + +    create_mutex(); +    pa_mutex_lock(mutex); + +    if (ref() < 0) +        ret = -1; +    else +        ret = pipe_fd[0]; + +    pa_mutex_unlock(mutex); + +    return ret; +} + +int pa_autospawn_lock_acquire(pa_bool_t block) { +    int ret = -1; + +    create_mutex(); +    pa_mutex_lock(mutex); +    pa_assert(n_ref >= 1); + +    pa_mutex_lock(lock_fd_mutex); + +    for (;;) { + +        empty_pipe(); + +        if (lock_fd >= 0 && !taken) { +            taken = TRUE; +            ret = 1; +            break; +        } + +        if (lock_fd < 0) +            if (start_thread() < 0) +                break; + +        if (!block) { +            ret = 0; +            break; +        } + +        pa_mutex_unlock(lock_fd_mutex); +        pa_mutex_unlock(mutex); + +        wait_for_ping(); + +        pa_mutex_lock(mutex); +        pa_mutex_lock(lock_fd_mutex); +    } + +    pa_mutex_unlock(lock_fd_mutex); + +    pa_mutex_unlock(mutex); + +    return ret; +} + +void pa_autospawn_lock_release(void) { + +    create_mutex(); +    pa_mutex_lock(mutex); +    pa_assert(n_ref >= 1); + +    pa_assert(taken); +    taken = FALSE; + +    ping(); + +    pa_mutex_unlock(mutex); +} + +void pa_autospawn_lock_done(pa_bool_t after_fork) { + +    create_mutex(); +    pa_mutex_lock(mutex); +    pa_assert(n_ref >= 1); + +    unref(after_fork); + +    pa_mutex_unlock(mutex); +} diff --git a/src/pulsecore/lock-autospawn.h b/src/pulsecore/lock-autospawn.h new file mode 100644 index 00000000..c04c4bd1 --- /dev/null +++ b/src/pulsecore/lock-autospawn.h @@ -0,0 +1,32 @@ +#ifndef foopulselockautospawnhfoo +#define foopulselockautospawnhfoo + +/*** +  This file is part of PulseAudio. + +  Copyright 2008 Lennart Poettering + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as +  published by the Free Software Foundation; either version 2.1 of the +  License, or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  Lesser General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public +  License along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#include <pulsecore/macro.h> + +int pa_autospawn_lock_init(void); +int pa_autospawn_lock_acquire(pa_bool_t block); +void pa_autospawn_lock_release(void); +void pa_autospawn_lock_done(pa_bool_t after_fork); + +#endif diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c index 5eda4f65..adf2f112 100644 --- a/src/pulsecore/log.c +++ b/src/pulsecore/log.c @@ -30,6 +30,10 @@  #include <string.h>  #include <errno.h> +#ifdef HAVE_EXECINFO_H +#include <execinfo.h> +#endif +  #ifdef HAVE_SYSLOG_H  #include <syslog.h>  #endif @@ -37,19 +41,27 @@  #include <pulse/utf8.h>  #include <pulse/xmalloc.h>  #include <pulse/util.h> +#include <pulse/timeval.h>  #include <pulsecore/macro.h>  #include <pulsecore/core-util.h> +#include <pulsecore/rtclock.h> +#include <pulsecore/once.h>  #include "log.h"  #define ENV_LOGLEVEL "PULSE_LOG"  #define ENV_LOGMETA "PULSE_LOG_META" +#define ENV_LOGTIME "PULSE_LOG_TIME" +#define ENV_LOGBACKTRACE "PULSE_LOG_BACKTRACE"  static char *log_ident = NULL, *log_ident_local = NULL;  static pa_log_target_t log_target = PA_LOG_STDERR; -static void (*user_log_func)(pa_log_level_t l, const char *s) = NULL; -static pa_log_level_t maximal_level = PA_LOG_NOTICE; +static pa_log_func_t user_log_func = NULL; +static pa_log_level_t maximal_level = PA_LOG_ERROR; +static unsigned show_backtrace = 0; +static pa_bool_t show_meta = FALSE; +static pa_bool_t show_time = FALSE;  #ifdef HAVE_SYSLOG_H  static const int level_to_syslog[] = { @@ -81,6 +93,9 @@ void pa_log_set_ident(const char *p) {  /* To make valgrind shut up. */  static void ident_destructor(void) PA_GCC_DESTRUCTOR;  static void ident_destructor(void) { +    if (!pa_in_valgrind()) +        return; +      pa_xfree(log_ident);      pa_xfree(log_ident_local);  } @@ -91,13 +106,81 @@ void pa_log_set_maximal_level(pa_log_level_t l) {      maximal_level = l;  } -void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t l, const char*s)) { +void pa_log_set_target(pa_log_target_t t, pa_log_func_t func) {      pa_assert(t == PA_LOG_USER || !func);      log_target = t;      user_log_func = func;  } +void pa_log_set_show_meta(pa_bool_t b) { +    show_meta = b; +} + +void pa_log_set_show_time(pa_bool_t b) { +    show_time = b; +} + +void pa_log_set_show_backtrace(unsigned nlevels) { +    show_backtrace = nlevels; +} + +#ifdef HAVE_EXECINFO_H + +static char* get_backtrace(unsigned show_nframes) { +    void* trace[32]; +    int n_frames; +    char **symbols, *e, *r; +    unsigned j, n; +    size_t a; + +    if (show_nframes <= 0) +        return NULL; + +    n_frames = backtrace(trace, PA_ELEMENTSOF(trace)); + +    if (n_frames <= 0) +        return NULL; + +    symbols = backtrace_symbols(trace, n_frames); + +    if (!symbols) +        return NULL; + +    n = PA_MIN((unsigned) n_frames, show_nframes); + +    a = 4; + +    for (j = 0; j < n; j++) { +        if (j > 0) +            a += 2; +        a += strlen(symbols[j]); +    } + +    r = pa_xnew(char, a); + +    strcpy(r, " ("); +    e = r + 2; + +    for (j = 0; j < n; j++) { +        if (j > 0) { +            strcpy(e, "<<"); +            e += 2; +        } + +        strcpy(e, symbols[j]); +        e += strlen(symbols[j]); +    } + +    strcpy(e, ")"); + +    free(symbols); + +    return r; +} + +#endif +  void pa_log_levelv_meta(          pa_log_level_t level,          const char*file, @@ -109,31 +192,82 @@ void pa_log_levelv_meta(      const char *e;      char *t, *n;      int saved_errno = errno; +    char *bt = NULL; +    pa_log_level_t ml; +#ifdef HAVE_EXECINFO_H +    unsigned show_bt; +#endif      /* We don't use dynamic memory allocation here to minimize the hit       * in RT threads */ -    char text[1024], location[128]; +    char text[4096], location[128], timestamp[32];      pa_assert(level < PA_LOG_LEVEL_MAX);      pa_assert(format); -    if ((e = getenv(ENV_LOGLEVEL))) -        maximal_level = atoi(e); +    ml = maximal_level; -    if (level > maximal_level) { +    if (PA_UNLIKELY((e = getenv(ENV_LOGLEVEL)))) { +        pa_log_level_t eml = (pa_log_level_t) atoi(e); + +        if (eml > ml) +            ml = eml; +    } + +    if (PA_LIKELY(level > ml)) {          errno = saved_errno;          return;      }      pa_vsnprintf(text, sizeof(text), format, ap); -    if (getenv(ENV_LOGMETA) && file && line > 0 && func) +    if ((show_meta || getenv(ENV_LOGMETA)) && file && line > 0 && func)          pa_snprintf(location, sizeof(location), "[%s:%i %s()] ", file, line, func);      else if (file)          pa_snprintf(location, sizeof(location), "%s: ", pa_path_get_filename(file));      else          location[0] = 0; +    if (show_time || getenv(ENV_LOGTIME)) { +        static pa_usec_t start, last; +        pa_usec_t u, a, r; + +        u = pa_rtclock_usec(); + +        PA_ONCE_BEGIN { +            start = u; +            last = u; +        } PA_ONCE_END; + +        r = u - last; +        a = u - start; + +        /* This is not thread safe, but this is a debugging tool only +         * anyway. */ +        last = u; + +        pa_snprintf(timestamp, sizeof(timestamp), "(%4llu.%03llu|%4llu.%03llu) ", +                    (unsigned long long) (a / PA_USEC_PER_SEC), +                    (unsigned long long) (((a / PA_USEC_PER_MSEC)) % 1000), +                    (unsigned long long) (r / PA_USEC_PER_SEC), +                    (unsigned long long) (((r / PA_USEC_PER_MSEC)) % 1000)); + +    } else +        timestamp[0] = 0; + +#ifdef HAVE_EXECINFO_H +    show_bt = show_backtrace; + +    if ((e = getenv(ENV_LOGBACKTRACE))) { +        unsigned ebt = (unsigned) atoi(e); + +        if (ebt > show_bt) +            show_bt = ebt; +    } + +    bt = get_backtrace(show_bt); +#endif +      if (!pa_utf8_valid(text))          pa_log_level(level, __FILE__": invalid UTF-8 string following below:"); @@ -148,19 +282,22 @@ void pa_log_levelv_meta(          switch (log_target) {              case PA_LOG_STDERR: { -                const char *prefix = "", *suffix = ""; +                const char *prefix = "", *suffix = "", *grey = "";                  char *local_t;  #ifndef OS_IS_WIN32                  /* Yes indeed. Useless, but fun! */                  if (isatty(STDERR_FILENO)) { -                    if (level <= PA_LOG_ERROR) { +                    if (level <= PA_LOG_ERROR)                          prefix = "\x1B[1;31m"; -                        suffix = "\x1B[0m"; -                    } else if (level <= PA_LOG_WARN) { +                    else if (level <= PA_LOG_WARN)                          prefix = "\x1B[1m"; + +                    if (bt) +                        grey = "\x1B[2m"; + +                    if (grey[0] || prefix[0])                          suffix = "\x1B[0m"; -                    }                  }  #endif @@ -168,9 +305,9 @@ void pa_log_levelv_meta(                   * minimize the hit in RT threads */                  local_t = pa_utf8_to_locale(t);                  if (!local_t) -                    fprintf(stderr, "%c: %s%s%s%s\n", level_to_char[level], location, prefix, t, suffix); +                    fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, grey, pa_strempty(bt), suffix);                  else { -                    fprintf(stderr, "%c: %s%s%s%s\n", level_to_char[level], location, prefix, local_t, suffix); +                    fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, local_t, grey, pa_strempty(bt), suffix);                      pa_xfree(local_t);                  } @@ -185,9 +322,9 @@ void pa_log_levelv_meta(                  local_t = pa_utf8_to_locale(t);                  if (!local_t) -                    syslog(level_to_syslog[level], "%s%s", location, t); +                    syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));                  else { -                    syslog(level_to_syslog[level], "%s%s", location, local_t); +                    syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, local_t, pa_strempty(bt));                      pa_xfree(local_t);                  } @@ -199,7 +336,7 @@ void pa_log_levelv_meta(              case PA_LOG_USER: {                  char x[1024]; -                pa_snprintf(x, sizeof(x), "%s%s", location, t); +                pa_snprintf(x, sizeof(x), "%s%s%s", timestamp, location, t);                  user_log_func(level, x);                  break; diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h index 2047696e..3d66e903 100644 --- a/src/pulsecore/log.h +++ b/src/pulsecore/log.h @@ -25,6 +25,8 @@  #include <stdarg.h>  #include <stdlib.h> + +#include <pulsecore/macro.h>  #include <pulse/gccmacro.h>  /* A simple logging subsystem */ @@ -49,11 +51,16 @@ typedef enum pa_log_level {  /* Set an identification for the current daemon. Used when logging to syslog. */  void pa_log_set_ident(const char *p); +typedef void (*pa_log_func_t)(pa_log_level_t t, const char*s); +  /* Set another log target. If t is PA_LOG_USER you may specify a function that is called every log string */ -void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t t, const char*s)); +void pa_log_set_target(pa_log_target_t t, pa_log_func_t func); -/* Minimal log level */ +/* Maximal log level */  void pa_log_set_maximal_level(pa_log_level_t l); +void pa_log_set_show_meta(pa_bool_t b); +void pa_log_set_show_time(pa_bool_t b); +void pa_log_set_show_backtrace(unsigned nlevels);  void pa_log_level_meta(          pa_log_level_t level, diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h index fd33b7bb..f9ce949a 100644 --- a/src/pulsecore/macro.h +++ b/src/pulsecore/macro.h @@ -30,7 +30,6 @@  #include <stdio.h>  #include <stdlib.h> -#include <pulsecore/log.h>  #include <pulse/gccmacro.h>  #ifndef PACKAGE @@ -40,7 +39,7 @@  #ifndef PA_LIKELY  #ifdef __GNUC__  #define PA_LIKELY(x) (__builtin_expect(!!(x),1)) -#define PA_UNLIKELY(x) (__builtin_expect((x),0)) +#define PA_UNLIKELY(x) (__builtin_expect(!!(x),0))  #else  #define PA_LIKELY(x) (x)  #define PA_UNLIKELY(x) (x) @@ -208,7 +207,7 @@ typedef int pa_bool_t;  #define PA_PATH_SEP_CHAR '/'  #endif -#ifdef __GNUC__ +#if defined(__GNUC__) && defined(__ELF__)  #define PA_WARN_REFERENCE(sym, msg)                  \      __asm__(".section .gnu.warning." #sym);          \ @@ -221,4 +220,13 @@ typedef int pa_bool_t;  #endif +#if defined(__i386__) || defined(__x86_64__) +#define PA_DEBUG_TRAP __asm__("int $3") +#else +#define PA_DEBUG_TRAP raise(SIGTRAP) +#endif + +/* We include this at the very last place */ +#include <pulsecore/log.h> +  #endif diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c index b43113d6..d9e1bf1c 100644 --- a/src/pulsecore/memblock.c +++ b/src/pulsecore/memblock.c @@ -31,6 +31,10 @@  #include <signal.h>  #include <errno.h> +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include <valgrind/memcheck.h> +#endif +  #include <pulse/xmalloc.h>  #include <pulse/def.h> @@ -158,14 +162,14 @@ static void stat_add(pa_memblock*b) {      pa_assert(b->pool);      pa_atomic_inc(&b->pool->stat.n_allocated); -    pa_atomic_add(&b->pool->stat.allocated_size, b->length); +    pa_atomic_add(&b->pool->stat.allocated_size, (int) b->length);      pa_atomic_inc(&b->pool->stat.n_accumulated); -    pa_atomic_add(&b->pool->stat.accumulated_size, b->length); +    pa_atomic_add(&b->pool->stat.accumulated_size, (int) b->length);      if (b->type == PA_MEMBLOCK_IMPORTED) {          pa_atomic_inc(&b->pool->stat.n_imported); -        pa_atomic_add(&b->pool->stat.imported_size, b->length); +        pa_atomic_add(&b->pool->stat.imported_size, (int) b->length);      }      pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]); @@ -181,14 +185,14 @@ static void stat_remove(pa_memblock *b) {      pa_assert(pa_atomic_load(&b->pool->stat.allocated_size) >= (int) b->length);      pa_atomic_dec(&b->pool->stat.n_allocated); -    pa_atomic_sub(&b->pool->stat.allocated_size,  b->length); +    pa_atomic_sub(&b->pool->stat.allocated_size, (int) b->length);      if (b->type == PA_MEMBLOCK_IMPORTED) {          pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);          pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);          pa_atomic_dec(&b->pool->stat.n_imported); -        pa_atomic_sub(&b->pool->stat.imported_size, b->length); +        pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);      }      pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]); @@ -248,7 +252,7 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {          if ((unsigned) (idx = pa_atomic_inc(&p->n_init)) >= p->n_blocks)              pa_atomic_dec(&p->n_init);          else -            slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * idx)); +            slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) idx));          if (!slot) {              pa_log_info("Pool full"); @@ -257,6 +261,12 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {          }      } +/* #ifdef HAVE_VALGRIND_MEMCHECK_H */ +/*     if (PA_UNLIKELY(pa_in_valgrind())) { */ +/*         VALGRIND_MALLOCLIKE_BLOCK(slot, p->block_size, 0, 0); */ +/*     } */ +/* #endif */ +      return slot;  } @@ -272,7 +282,7 @@ static unsigned mempool_slot_idx(pa_mempool *p, void *ptr) {      pa_assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr);      pa_assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size); -    return ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size; +    return (unsigned) ((size_t) ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size);  }  /* No lock necessary */ @@ -519,13 +529,19 @@ static void memblock_free(pa_memblock *b) {          case PA_MEMBLOCK_POOL_EXTERNAL:          case PA_MEMBLOCK_POOL: {              struct mempool_slot *slot; -            int call_free; +            pa_bool_t call_free;              slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data));              pa_assert(slot);              call_free = b->type == PA_MEMBLOCK_POOL_EXTERNAL; +/* #ifdef HAVE_VALGRIND_MEMCHECK_H */ +/*             if (PA_UNLIKELY(pa_in_valgrind())) { */ +/*                 VALGRIND_FREELIKE_BLOCK(slot, b->pool->block_size); */ +/*             } */ +/* #endif */ +              /* The free list dimensions should easily allow all slots               * to fit in, hence try harder if pushing this slot into               * the free list fails */ @@ -647,7 +663,7 @@ static void memblock_replace_import(pa_memblock *b) {      pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);      pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);      pa_atomic_dec(&b->pool->stat.n_imported); -    pa_atomic_sub(&b->pool->stat.imported_size, b->length); +    pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);      seg = b->per_type.imported.segment;      pa_assert(seg); @@ -668,8 +684,9 @@ static void memblock_replace_import(pa_memblock *b) {          pa_mutex_unlock(seg->import->mutex);  } -pa_mempool* pa_mempool_new(pa_bool_t shared) { +pa_mempool* pa_mempool_new(pa_bool_t shared, size_t size) {      pa_mempool *p; +    char t1[64], t2[64];      p = pa_xnew(pa_mempool, 1); @@ -680,13 +697,26 @@ pa_mempool* pa_mempool_new(pa_bool_t shared) {      if (p->block_size < PA_PAGE_SIZE)          p->block_size = PA_PAGE_SIZE; -    p->n_blocks = PA_MEMPOOL_SLOTS_MAX; +    if (size <= 0) +        p->n_blocks = PA_MEMPOOL_SLOTS_MAX; +    else { +        p->n_blocks = (unsigned) (size / p->block_size); + +        if (p->n_blocks < 2) +            p->n_blocks = 2; +    }      if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) {          pa_xfree(p);          return NULL;      } +    pa_log_debug("Using %s memory pool with %u slots of size %s each, total size is %s", +                 p->memory.shared ? "shared" : "private", +                 p->n_blocks, +                 pa_bytes_snprint(t1, sizeof(t1), (unsigned) p->block_size), +                 pa_bytes_snprint(t2, sizeof(t2), (unsigned) (p->n_blocks * p->block_size))); +      memset(&p->stat, 0, sizeof(p->stat));      pa_atomic_store(&p->n_init, 0); @@ -754,7 +784,7 @@ void pa_mempool_vacuum(pa_mempool *p) {              ;      while ((slot = pa_flist_pop(list))) { -        pa_shm_punch(&p->memory, (uint8_t*) slot - (uint8_t*) p->memory.ptr, p->block_size); +        pa_shm_punch(&p->memory, (size_t) ((uint8_t*) slot - (uint8_t*) p->memory.ptr), p->block_size);          while (pa_flist_push(p->free_slots, slot))              ; @@ -967,7 +997,7 @@ void pa_memexport_free(pa_memexport *e) {      pa_mutex_lock(e->mutex);      while (e->used_slots) -        pa_memexport_process_release(e, e->used_slots - e->slots); +        pa_memexport_process_release(e, (uint32_t) (e->used_slots - e->slots));      pa_mutex_unlock(e->mutex);      pa_mutex_lock(e->pool->mutex); @@ -1006,7 +1036,7 @@ int pa_memexport_process_release(pa_memexport *e, uint32_t id) {      pa_assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) b->length);      pa_atomic_dec(&e->pool->stat.n_exported); -    pa_atomic_sub(&e->pool->stat.exported_size, b->length); +    pa_atomic_sub(&e->pool->stat.exported_size, (int) b->length);      pa_memblock_unref(b); @@ -1034,7 +1064,7 @@ static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {              slot->block->per_type.imported.segment->import != i)              continue; -        idx = slot - e->slots; +        idx = (uint32_t) (slot - e->slots);          e->revoke_cb(e, idx, e->userdata);          pa_memexport_process_release(e, idx);      } @@ -1095,7 +1125,7 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32      PA_LLIST_PREPEND(struct memexport_slot, e->used_slots, slot);      slot->block = b; -    *block_id = slot - e->slots; +    *block_id = (uint32_t) (slot - e->slots);      pa_mutex_unlock(e->mutex);  /*     pa_log("Got block id %u", *block_id); */ @@ -1115,13 +1145,13 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32      pa_assert((uint8_t*) data + b->length <= (uint8_t*) memory->ptr + memory->size);      *shm_id = memory->id; -    *offset = (uint8_t*) data - (uint8_t*) memory->ptr; +    *offset = (size_t) ((uint8_t*) data - (uint8_t*) memory->ptr);      *size = b->length;      pa_memblock_release(b);      pa_atomic_inc(&e->pool->stat.n_exported); -    pa_atomic_add(&e->pool->stat.exported_size, b->length); +    pa_atomic_add(&e->pool->stat.exported_size, (int) b->length);      return 0;  } diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h index efe55b02..b1eab2a9 100644 --- a/src/pulsecore/memblock.h +++ b/src/pulsecore/memblock.h @@ -117,7 +117,7 @@ pa_mempool * pa_memblock_get_pool(pa_memblock *b);  pa_memblock *pa_memblock_will_need(pa_memblock *b);  /* The memory block manager */ -pa_mempool* pa_mempool_new(pa_bool_t shared); +pa_mempool* pa_mempool_new(pa_bool_t shared, size_t size);  void pa_mempool_free(pa_mempool *p);  const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p);  void pa_mempool_vacuum(pa_mempool *p); diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c index 841b9075..265da37f 100644 --- a/src/pulsecore/memblockq.c +++ b/src/pulsecore/memblockq.c @@ -84,7 +84,8 @@ pa_memblockq* pa_memblockq_new(      pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",                   (unsigned long) maxlength, (unsigned long) tlength, (unsigned long) base, (unsigned long) prebuf, (unsigned long) minreq, (unsigned long) maxrewind); -    bq->missing = bq->requested = bq->maxlength = bq->tlength = bq->prebuf = bq->minreq = bq->maxrewind = 0; +    bq->missing = 0; +    bq->requested = bq->maxlength = bq->tlength = bq->prebuf = bq->minreq = bq->maxrewind = 0;      bq->in_prebuf = TRUE;      pa_memblockq_set_maxlength(bq, maxlength); @@ -215,7 +216,7 @@ static void drop_backlog(pa_memblockq *bq) {      int64_t boundary;      pa_assert(bq); -    boundary = bq->read_index - bq->maxrewind; +    boundary = bq->read_index - (int64_t) bq->maxrewind;      while (bq->blocks && (bq->blocks->index + (int64_t) bq->blocks->chunk.length <= boundary))          drop_block(bq, bq->blocks); @@ -227,10 +228,10 @@ static pa_bool_t can_push(pa_memblockq *bq, size_t l) {      pa_assert(bq);      if (bq->read_index > bq->write_index) { -        size_t d =  bq->read_index - bq->write_index; +        int64_t d = bq->read_index - bq->write_index; -        if (l > d) -            l -= d; +        if ((int64_t) l > d) +            l -= (size_t) d;          else              return TRUE;      } @@ -239,7 +240,7 @@ static pa_bool_t can_push(pa_memblockq *bq, size_t l) {      /* Make sure that the list doesn't get too long */      if (bq->write_index + (int64_t) l > end) -        if (bq->write_index + l - bq->read_index > bq->maxlength) +        if (bq->write_index + (int64_t) l - bq->read_index > (int64_t) bq->maxlength)              return FALSE;      return TRUE; @@ -294,7 +295,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {              /* This entry isn't touched at all, let's skip it */              q = q->prev;          } else if (bq->write_index <= q->index && -            bq->write_index + chunk.length >= q->index + q->chunk.length) { +                   bq->write_index + (int64_t) chunk.length >= q->index + (int64_t) q->chunk.length) {              /* This entry is fully replaced by the new entry, so let's drop it */ @@ -306,7 +307,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {              /* The write index points into this memblock, so let's               * truncate or split it */ -            if (bq->write_index + chunk.length < q->index + q->chunk.length) { +            if (bq->write_index + (int64_t) chunk.length < q->index + (int64_t) q->chunk.length) {                  /* We need to save the end of this memchunk */                  struct list_item *p; @@ -320,11 +321,11 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {                  pa_memblock_ref(p->chunk.memblock);                  /* Calculate offset */ -                d = bq->write_index + chunk.length - q->index; +                d = (size_t) (bq->write_index + (int64_t) chunk.length - q->index);                  pa_assert(d > 0);                  /* Drop it from the new entry */ -                p->index = q->index + d; +                p->index = q->index + (int64_t) d;                  p->chunk.length -= d;                  /* Add it to the list */ @@ -339,7 +340,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {              }              /* Truncate the chunk */ -            if (!(q->chunk.length = bq->write_index - q->index)) { +            if (!(q->chunk.length = (size_t) (bq->write_index - q->index))) {                  struct list_item *p;                  p = q;                  q = q->prev; @@ -357,8 +358,8 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {              /* The job overwrites the current entry at the end, so let's drop the beginning of this entry */ -            d = bq->write_index + chunk.length - q->index; -            q->index += d; +            d = (size_t) (bq->write_index + (int64_t) chunk.length - q->index); +            q->index += (int64_t) d;              q->chunk.index += d;              q->chunk.length -= d; @@ -373,11 +374,11 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {          /* Try to merge memory blocks */          if (q->chunk.memblock == chunk.memblock && -            q->chunk.index + (int64_t)q->chunk.length == chunk.index && -            bq->write_index == q->index + (int64_t)q->chunk.length) { +            q->chunk.index + q->chunk.length == chunk.index && +            bq->write_index == q->index + (int64_t) q->chunk.length) {              q->chunk.length += chunk.length; -            bq->write_index += chunk.length; +            bq->write_index += (int64_t) chunk.length;              goto finish;          }      } else @@ -389,7 +390,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {      n->chunk = chunk;      pa_memblock_ref(n->chunk.memblock);      n->index = bq->write_index; -    bq->write_index += n->chunk.length; +    bq->write_index += (int64_t) n->chunk.length;      n->next = q ? q->next : bq->blocks;      n->prev = q; @@ -411,10 +412,10 @@ finish:      delta = bq->write_index - old;      if (delta >= (int64_t) bq->requested) { -        delta -= bq->requested; +        delta -= (int64_t) bq->requested;          bq->requested = 0;      } else { -        bq->requested -= delta; +        bq->requested -= (size_t) delta;          delta = 0;      } @@ -471,7 +472,7 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {          /* How much silence shall we return? */          if (bq->current_read) -            length = bq->current_read->index - bq->read_index; +            length = (size_t) (bq->current_read->index - bq->read_index);          else if (bq->write_index > bq->read_index)              length = (size_t) (bq->write_index - bq->read_index);          else @@ -506,8 +507,8 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {      pa_assert(bq->read_index >= bq->current_read->index);      d = bq->read_index - bq->current_read->index; -    chunk->index += d; -    chunk->length -= d; +    chunk->index += (size_t) d; +    chunk->length -= (size_t) d;      return 0;  } @@ -533,20 +534,20 @@ void pa_memblockq_drop(pa_memblockq *bq, size_t length) {              /* We go through this piece by piece to make sure we don't               * drop more than allowed by prebuf */ -            p = bq->current_read->index + bq->current_read->chunk.length; +            p = bq->current_read->index + (int64_t) bq->current_read->chunk.length;              pa_assert(p >= bq->read_index);              d = p - bq->read_index;              if (d > (int64_t) length) -                d = length; +                d = (int64_t) length;              bq->read_index += d; -            length -= d; +            length -= (size_t) d;          } else {              /* The list is empty, there's nothing we could drop */ -            bq->read_index += length; +            bq->read_index += (int64_t) length;              break;          }      } @@ -563,8 +564,8 @@ void pa_memblockq_rewind(pa_memblockq *bq, size_t length) {      /* This is kind of the inverse of pa_memblockq_drop() */ -    bq->read_index -= length; -    bq->missing -= length; +    bq->read_index -= (int64_t) length; +    bq->missing -= (int64_t) length;  }  pa_bool_t pa_memblockq_is_readable(pa_memblockq *bq) { @@ -628,10 +629,10 @@ void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {      delta = bq->write_index - old;      if (delta >= (int64_t) bq->requested) { -        delta -= bq->requested; +        delta -= (int64_t) bq->requested;          bq->requested = 0;      } else if (delta >= 0) { -        bq->requested -= delta; +        bq->requested -= (size_t) delta;          delta = 0;      } @@ -652,10 +653,10 @@ void pa_memblockq_flush_write(pa_memblockq *bq) {      delta = bq->write_index - old;      if (delta >= (int64_t) bq->requested) { -        delta -= bq->requested; +        delta -= (int64_t) bq->requested;          bq->requested = 0;      } else if (delta >= 0) { -        bq->requested -= delta; +        bq->requested -= (size_t) delta;          delta = 0;      } @@ -874,7 +875,7 @@ int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {              pa_memblock_unref(chunk.memblock);          } else -            pa_memblockq_seek(bq, chunk.length, PA_SEEK_RELATIVE); +            pa_memblockq_seek(bq, (int64_t) chunk.length, PA_SEEK_RELATIVE);          pa_memblockq_drop(bq, chunk.length);      } diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h index 4b9450f6..31f908df 100644 --- a/src/pulsecore/memblockq.h +++ b/src/pulsecore/memblockq.h @@ -155,7 +155,7 @@ void pa_memblockq_set_maxlength(pa_memblockq *memblockq, size_t maxlength); /* m  void pa_memblockq_set_tlength(pa_memblockq *memblockq, size_t tlength); /* might modify minreq, too */  void pa_memblockq_set_prebuf(pa_memblockq *memblockq, size_t prebuf); /* might modify minreq, too */  void pa_memblockq_set_minreq(pa_memblockq *memblockq, size_t minreq); -void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t rewind); /* Set the maximum history size */ +void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t maxrewind); /* Set the maximum history size */  void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memchunk *silence);  /* Call pa_memchunk_willneed() for every chunk in the queue from the current read pointer to the end */ diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c index d257b4ce..9e60125e 100644 --- a/src/pulsecore/modargs.c +++ b/src/pulsecore/modargs.c @@ -183,7 +183,7 @@ fail:      return NULL;  } -static void free_func(void *p, PA_GCC_UNUSED void*userdata) { +static void free_func(void *p, void*userdata) {      struct entry *e = p;      pa_assert(e); diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c index dbafa8c9..9b17cb91 100644 --- a/src/pulsecore/module.c +++ b/src/pulsecore/module.c @@ -48,7 +48,7 @@  #define UNLOAD_POLL_TIME 2 -static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { +static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) {      pa_core *c = PA_CORE(userdata);      struct timeval ntv; @@ -59,7 +59,7 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED      pa_module_unload_unused(c);      pa_gettimeofday(&ntv); -    pa_timeval_add(&ntv, UNLOAD_POLL_TIME*1000000); +    pa_timeval_add(&ntv, UNLOAD_POLL_TIME*PA_USEC_PER_SEC);      m->time_restart(e, &ntv);  } @@ -124,7 +124,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {      if (m->auto_unload && !c->module_auto_unload_event) {          struct timeval ntv;          pa_gettimeofday(&ntv); -        pa_timeval_add(&ntv, UNLOAD_POLL_TIME*1000000); +        pa_timeval_add(&ntv, UNLOAD_POLL_TIME*PA_USEC_PER_SEC);          c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);      } @@ -190,7 +190,7 @@ void pa_module_unload_by_index(pa_core *c, uint32_t idx, pa_bool_t force) {      pa_assert(c);      pa_assert(idx != PA_IDXSET_INVALID); -    if (m->core->disallow_module_loading && !force) +    if (c->disallow_module_loading && !force)          return;      if (!(m = pa_idxset_remove_by_index(c->modules, idx))) diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c index 30420546..ecd8def8 100644 --- a/src/pulsecore/namereg.c +++ b/src/pulsecore/namereg.c @@ -45,12 +45,13 @@ struct namereg_entry {      void *data;  }; -static int is_valid_char(char c) { +static pa_bool_t is_valid_char(char c) {      return          (c >= 'a' && c <= 'z') ||          (c >= 'A' && c <= 'Z') ||          (c >= '0' && c <= '9') ||          c == '.' || +        c == '-' ||          c == '_';  } @@ -77,10 +78,10 @@ char* pa_namereg_make_valid_name(const char *name) {      if (*name == 0)          return NULL; -    n = pa_xnew(char, strlen(name)+1); +    n = pa_xmalloc(strlen(name)+1);      for (a = name, b = n; *a && (a-name < PA_NAME_MAX); a++, b++) -        *b = is_valid_char(*a) ? *a : '_'; +        *b = (char) (is_valid_char(*a) ? *a : '_');      *b = 0; @@ -97,7 +98,7 @@ void pa_namereg_free(pa_core *c) {      pa_hashmap_free(c->namereg, NULL, NULL);  } -const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail) { +const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, pa_bool_t fail) {      struct namereg_entry *e;      char *n = NULL; @@ -136,7 +137,7 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t              return NULL;          } -        k = pa_xnew(char, l+4); +        k = pa_xmalloc(l+4);          for (i = 2; i <= 99; i++) {              pa_snprintf(k, l+4, "%s.%u", name, i); diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h index 3c1de8e7..f4581006 100644 --- a/src/pulsecore/namereg.h +++ b/src/pulsecore/namereg.h @@ -35,7 +35,7 @@ typedef enum pa_namereg_type {  void pa_namereg_free(pa_core *c); -const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail); +const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, pa_bool_t fail);  void pa_namereg_unregister(pa_core *c, const char *name);  void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload);  int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type); diff --git a/src/pulsecore/object.h b/src/pulsecore/object.h index 7dcfa2eb..2ee4fc31 100644 --- a/src/pulsecore/object.h +++ b/src/pulsecore/object.h @@ -42,7 +42,7 @@ struct pa_object {  pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name));  #define pa_object_new(type) ((type*) pa_object_new_internal(sizeof(type), #type, type##_check_type) -#define pa_object_free ((void (*) (pa_object* o)) pa_xfree) +#define pa_object_free ((void (*) (pa_object* _obj)) pa_xfree)  int pa_object_check_type(const char *type); diff --git a/src/pulsecore/once.c b/src/pulsecore/once.c index 989741dc..3d4543cb 100644 --- a/src/pulsecore/once.c +++ b/src/pulsecore/once.c @@ -28,13 +28,13 @@  #include "once.h" -int pa_once_begin(pa_once *control) { +pa_bool_t pa_once_begin(pa_once *control) {      pa_mutex *m;      pa_assert(control);      if (pa_atomic_load(&control->done)) -        return 0; +        return FALSE;      pa_atomic_inc(&control->ref); @@ -50,15 +50,17 @@ int pa_once_begin(pa_once *control) {               * wait until it is unlocked */              pa_mutex_lock(m); +            pa_assert(pa_atomic_load(&control->done)); +              pa_once_end(control); -            return 0; +            return FALSE;          }          pa_assert_se(m = pa_mutex_new(FALSE, FALSE));          pa_mutex_lock(m);          if (pa_atomic_ptr_cmpxchg(&control->mutex, NULL, m)) -            return 1; +            return TRUE;          pa_mutex_unlock(m);          pa_mutex_free(m); @@ -91,4 +93,3 @@ void pa_run_once(pa_once *control, pa_once_func_t func) {          pa_once_end(control);      }  } - diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h index 576d40fa..c0191ef0 100644 --- a/src/pulsecore/once.h +++ b/src/pulsecore/once.h @@ -38,7 +38,7 @@ typedef struct pa_once {      }  /* Not to be called directly, use the macros defined below instead */ -int pa_once_begin(pa_once *o); +pa_bool_t pa_once_begin(pa_once *o);  void pa_once_end(pa_once *o);  #define PA_ONCE_BEGIN                                                   \ diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c index f2b6b2cf..c5cd7fe7 100644 --- a/src/pulsecore/parseaddr.c +++ b/src/pulsecore/parseaddr.c @@ -51,20 +51,29 @@ static char *parse_host(const char *s, uint16_t *ret_port) {          if (!(e = strchr(s+1, ']')))              return NULL; -        if (e[1] == ':') -            *ret_port = atoi(e+2); -        else if (e[1] != 0) +        if (e[1] == ':') { +            uint32_t p; + +            if (pa_atou(e+2, &p) < 0) +                return NULL; + +            *ret_port = (uint16_t) p; +        } else if (e[1] != 0)              return NULL; -        return pa_xstrndup(s+1, e-s-1); +        return pa_xstrndup(s+1, (size_t) (e-s-1));      } else {          char *e; +        uint32_t p;          if (!(e = strrchr(s, ':')))              return pa_xstrdup(s); -        *ret_port = atoi(e+1); -        return pa_xstrndup(s, e-s); +        if (pa_atou(e+1, &p) < 0) +            return NULL; + +        *ret_port = (uint16_t) p; +        return pa_xstrndup(s, (size_t) (e-s));      }  } diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c index e6a6ae4d..00df0f79 100644 --- a/src/pulsecore/pdispatch.c +++ b/src/pulsecore/pdispatch.c @@ -255,7 +255,7 @@ finish:      return ret;  } -static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { +static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata) {      struct reply_info*r = userdata;      pa_assert(r); diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c index addb17cc..bf9ba983 100644 --- a/src/pulsecore/pid.c +++ b/src/pulsecore/pid.c @@ -73,6 +73,7 @@ static pid_t read_pid(const char *fn, int fd) {      if (pa_atou(t, &pid) < 0) {          pa_log_warn("Failed to parse PID file '%s'", fn); +        errno = EINVAL;          return (pid_t) -1;      } @@ -110,7 +111,7 @@ static int open_pid_file(const char *fn, int mode) {              goto fail;          } -        /* Does the file still exist in the file system? When ye, w're done, otherwise restart */ +        /* Does the file still exist in the file system? When yes, we're done, otherwise restart */          if (st.st_nlink >= 1)              break; @@ -131,8 +132,10 @@ static int open_pid_file(const char *fn, int mode) {  fail:      if (fd >= 0) { +        int saved_errno = errno;          pa_lock_fd(fd, 0);          pa_close(fd); +        errno = saved_errno;      }      return -1; @@ -154,8 +157,11 @@ static int proc_name_ours(pid_t pid, const char *procname) {          char stored[64];          if (!(fgets(stored, sizeof(stored), f))) { +            int saved_errno = feof(f) ? EINVAL : errno;              pa_log_info("Failed to read from %s: %s", bn, feof(f) ? "EOF" : pa_cstrerror(errno));              fclose(f); + +            errno = saved_errno;              return -1;          } @@ -165,20 +171,22 @@ static int proc_name_ours(pid_t pid, const char *procname) {          good = pa_startswith(stored, expected);          pa_xfree(expected); -#if !defined(__OPTIMIZE__) +/*#if !defined(__OPTIMIZE__)*/          if (!good) {              /* libtool likes to rename our binary names ... */              expected = pa_sprintf_malloc("%lu (lt-%s)", (unsigned long) pid, procname);              good = pa_startswith(stored, expected);              pa_xfree(expected);          } -#endif +/*#endif*/          return !!good;      } -#endif +#else      return 1; +#endif +  }  /* Create a new PID file for the current process. */ @@ -203,6 +211,7 @@ int pa_pid_file_create(const char *procname) {      if ((pid = read_pid(fn, fd)) == (pid_t) -1)          pa_log_warn("Corrupt PID file, overwriting.");      else if (pid > 0) { +        int ours = 1;  #ifdef OS_IS_WIN32          if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) { @@ -210,11 +219,13 @@ int pa_pid_file_create(const char *procname) {  #else          if (kill(pid, 0) >= 0 || errno != ESRCH) {  #endif -            int ours = 1;              if (procname) -                if ((ours = proc_name_ours(pid, procname)) < 0) +                if ((ours = proc_name_ours(pid, procname)) < 0) { +                    pa_log_warn("Could not check to see if pid %lu is a pulseaudio process. " +                                "Asssuming it is and the daemon is already running.", (unsigned long) pid);                      goto fail; +                }              if (ours) {                  pa_log("Daemon already running."); @@ -227,7 +238,7 @@ int pa_pid_file_create(const char *procname) {      }      /* Overwrite the current PID file */ -    if (lseek(fd, 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, 0) < 0) { +    if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, (off_t) 0) < 0) {          pa_log("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));          goto fail;      } @@ -280,7 +291,7 @@ int pa_pid_file_remove(void) {          goto fail;      } -    if (ftruncate(fd, 0) < 0) { +    if (ftruncate(fd, (off_t) 0) < 0) {          pa_log_warn("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));          goto fail;      } @@ -342,8 +353,13 @@ int pa_pid_file_kill(int sig, pid_t *pid, const char *procname) {      if (!(fn = pa_runtime_path("pid")))          goto fail; -    if ((fd = open_pid_file(fn, O_RDONLY)) < 0) +    if ((fd = open_pid_file(fn, O_RDONLY)) < 0) { + +        if (errno == ENOENT) +            errno = ESRCH; +          goto fail; +    }      if ((*pid = read_pid(fn, fd)) == (pid_t) -1)          goto fail; @@ -354,8 +370,10 @@ int pa_pid_file_kill(int sig, pid_t *pid, const char *procname) {          if ((ours = proc_name_ours(*pid, procname)) < 0)              goto fail; -        if (!ours) +        if (!ours) { +            errno = ESRCH;              goto fail; +        }      }      ret = kill(*pid, sig); @@ -363,8 +381,10 @@ int pa_pid_file_kill(int sig, pid_t *pid, const char *procname) {  fail:      if (fd >= 0) { +        int saved_errno = errno;          pa_lock_fd(fd, 0);          pa_close(fd); +        errno = saved_errno;      }  #ifdef __linux__ diff --git a/src/pulsecore/prioq.c b/src/pulsecore/prioq.c new file mode 100644 index 00000000..693dc517 --- /dev/null +++ b/src/pulsecore/prioq.c @@ -0,0 +1,256 @@ +/*** +  This file is part of PulseAudio. + +  Copyright 2008 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 <pulse/xmalloc.h> + +#include <pulsecore/flist.h> + +#include "prioq.h" + +struct pa_prioq_item { +    void *value; +    unsigned idx; +}; + +struct pa_prioq { +    pa_prioq_item **items; +    unsigned n_items; +    unsigned n_allocated; +    pa_compare_func_t compare_func; +}; + +PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree); + +pa_prioq *pa_prioq_new(pa_compare_func_t compare_func) { + +    pa_prioq *q; + +    q = pa_xnew(pa_prioq, 1); +    q->compare_func = compare_func; +    q->n_items = 0; +    q->n_allocated = 64; +    q->items = pa_xnew(pa_prioq_item*, q->n_allocated); + +    return q; +} + +void pa_prioq_free(pa_prioq *q, pa_free2_cb_t free_cb, void *userdata) { +    pa_prioq_item **i, **e; + +    pa_assert(q); + +    for (i = q->items, e = q->items + q->n_items; i < e; i++) { + +        if (!*i) +            continue; + +        if (free_cb) +            free_cb((*i)->value, userdata); + +        pa_xfree(*i); +    } + +    pa_xfree(q->items); +    pa_xfree(q); +} + +static void shuffle_up(pa_prioq *q, pa_prioq_item *i) { +    unsigned j; + +    pa_assert(q); +    pa_assert(i); + +    j = i->idx; + +    while (j > 0) { +        unsigned k; + +        k = (j-1)/2; + +        if (q->compare_func(q->items[k]->value, i->value) < 0) +            break; + +        q->items[k]->idx = j; +        q->items[j] = q->items[k]; + +        j = k; +    } + +    i->idx = j; +    q->items[j] = i; + +} + +pa_prioq_item* pa_prioq_put(pa_prioq *q, void *p) { +    pa_prioq_item *i; + +    pa_assert(q); + +    if (q->n_items >= q->n_allocated) { +        q->n_allocated = PA_MAX(q->n_items+1, q->n_allocated)*2; +        q->items = pa_xrealloc(q->items, sizeof(pa_prioq_item*) * q->n_allocated); +    } + +    if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items)))) +        i = pa_xnew(pa_prioq_item, 1); + +    i->value = p; +    i->idx = q->n_items++; + +    shuffle_up(q, i); + +    return i; +} + +void* pa_prioq_peek(pa_prioq *q) { +    pa_assert(q); + +    if (q->n_items <= 0) +        return NULL; + +    return q->items[0]->value; +} + +void* pa_prioq_pop(pa_prioq *q){ +    pa_assert(q); + +    if (q->n_items <= 0) +        return NULL; + +    return pa_prioq_remove(q, q->items[0]); +} + +static void swap(pa_prioq *q, unsigned j, unsigned k) { +    pa_prioq_item *t; + +    pa_assert(q); +    pa_assert(j < q->n_items); +    pa_assert(k < q->n_items); + +    pa_assert(q->items[j]->idx == j); +    pa_assert(q->items[k]->idx == k); + +    t = q->items[j]; + +    q->items[j]->idx = k; +    q->items[j] = q->items[k]; + +    q->items[k]->idx = j; +    q->items[k] = t; +} + +static void shuffle_down(pa_prioq *q, unsigned idx) { + +    pa_assert(q); +    pa_assert(idx < q->n_items); + +    for (;;) { +        unsigned j, k, s; + +        k = (idx+1)*2; /* right child */ +        j = k-1;       /* left child */ + +        if (j >= q->n_items) +            break; + +        if (q->compare_func(q->items[j]->value, q->items[idx]->value) < 0) + +            /* So our left child is smaller than we are, let's +             * remember this fact */ +            s = j; +        else +            s = idx; + +        if (k < q->n_items && +            q->compare_func(q->items[k]->value, q->items[s]->value) < 0) + +            /* So our right child is smaller than we are, let's +             * remember this fact */ +            s = k; + +        /* s now points to the smallest of the three items */ + +        if (s == idx) +            /* No swap necessary, we're done */ +            break; + +        swap(q, idx, s); +        idx = s; +    } +} + +void* pa_prioq_remove(pa_prioq *q, pa_prioq_item *i) { +    void *p; + +    pa_assert(q); +    pa_assert(i); +    pa_assert(q->n_items >= 1); + +    p = i->value; + +    if (q->n_items-1 == i->idx) { +        /* We are the last entry, so let's just remove us and good */ +        q->n_items--; + +    } else { + +        /* We are not the last entry, we need to replace ourselves +         * with the last node and reshuffle */ + +        q->items[i->idx] = q->items[q->n_items-1]; +        q->items[i->idx]->idx = i->idx; +        q->n_items--; + +        shuffle_down(q, i->idx); +    } + +    if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0) +        pa_xfree(i); + +    return p; +} + +unsigned pa_prioq_size(pa_prioq *q) { +    pa_assert(q); + +    return q->n_items; +} + +pa_bool_t pa_prioq_isempty(pa_prioq *q) { +    pa_assert(q); + +    return q->n_items == 0; +} + +void pa_prioq_reshuffle(pa_prioq *q, pa_prioq_item *i) { +    pa_assert(q); +    pa_assert(i); + +    /* This will move the entry down as far as necessary */ +    shuffle_down(q, i->idx); + +    /* And this will move the entry up as far as necessary */ +    shuffle_up(q, i); +} diff --git a/src/pulsecore/prioq.h b/src/pulsecore/prioq.h new file mode 100644 index 00000000..fd3550b7 --- /dev/null +++ b/src/pulsecore/prioq.h @@ -0,0 +1,64 @@ +#ifndef foopulsecoreprioqhfoo +#define foopulsecoreprioqhfoo + +/*** +  This file is part of PulseAudio. + +  Copyright 2008 Lennart Poettering + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as +  published by the Free Software Foundation; either version 2.1 of the +  License, or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  Lesser General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public +  License along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#include <inttypes.h> + +#include <pulsecore/macro.h> +#include <pulsecore/idxset.h> + +/* A heap-based priority queue. Removal and insertion is O(log + * n). Removal can happen a the top or at any position referenced by a + * pa_prioq_item.  */ + +typedef struct pa_prioq pa_prioq; +typedef struct pa_prioq_item pa_prioq_item; + +/* Instantiate a new prioq with the specified comparison functions */ +pa_prioq* pa_prioq_new(pa_compare_func_t compare_func); + +/* Free the prioq. When the prioq is not empty the specified function is called for every entry contained */ +void pa_prioq_free(pa_prioq *q, pa_free2_cb_t free_cb, void *userdata); + +/* Store a new item in the prioq. */ +pa_prioq_item* pa_prioq_put(pa_prioq *q, void* data); + +/* Get the item on the top of the queue, but don't remove it from the queue*/ +void* pa_prioq_peek(pa_prioq*q); + +/* Get the item on the top of the queue, and remove it from thq queue */ +void* pa_prioq_pop(pa_prioq*q); + +/* Remove an arbitrary from theq prioq, returning it's data */ +void* pa_prioq_remove(pa_prioq*q, pa_prioq_item *i); + +/* The priority of an item was modified. Adjustthe queue to that */ +void pa_prioq_reshuffle(pa_prioq *q, pa_prioq_item *i); + +/* Return the current number of items in the prioq */ +unsigned pa_prioq_size(pa_prioq*s); + +/* Return TRUE of the prioq is empty */ +pa_bool_t pa_prioq_isempty(pa_prioq *s); + +#endif diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c index 6005775e..4d505f57 100644 --- a/src/pulsecore/proplist-util.c +++ b/src/pulsecore/proplist-util.c @@ -37,7 +37,7 @@  void pa_init_proplist(pa_proplist *p) {      int a, b; -#ifndef HAVE_DECL_ENVIRON +#if !HAVE_DECL_ENVIRON      extern char **environ;  #endif      char **e; diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index 4f121a3a..460119a9 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -184,7 +184,7 @@ static struct proto_handler proto_map[ESD_PROTO_MAX] = {      { sizeof(int),                    esd_proto_sample_free_or_play, "sample play" },                /* 8 */      { sizeof(int),                    NULL, "sample loop" },      { sizeof(int),                    NULL, "sample stop" }, -    { -1,                             NULL, "TODO: sample kill" }, +    { (size_t) -1,                    NULL, "TODO: sample kill" },      { ESD_KEY_LEN + sizeof(int),      esd_proto_standby_or_resume, "standby" },  /* NOOP! */      { ESD_KEY_LEN + sizeof(int),      esd_proto_standby_or_resume, "resume" },   /* NOOP! */         /* 13 */ @@ -194,8 +194,8 @@ static struct proto_handler proto_map[ESD_PROTO_MAX] = {      { sizeof(int),                    esd_proto_server_info, "server info" },      { sizeof(int),                    esd_proto_all_info, "all info" }, -    { -1,                             NULL, "TODO: subscribe" }, -    { -1,                             NULL, "TODO: unsubscribe" }, +    { (size_t) -1,                    NULL, "TODO: subscribe" }, +    { (size_t) -1,                    NULL, "TODO: unsubscribe" },      { 3 * sizeof(int),                esd_proto_stream_pan, "stream pan"},      { 3 * sizeof(int),                NULL, "sample pan" }, @@ -309,7 +309,7 @@ static void connection_write(connection *c, const void *data, size_t length) {  static void format_esd2native(int format, pa_bool_t swap_bytes, pa_sample_spec *ss) {      pa_assert(ss); -    ss->channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1; +    ss->channels = (uint8_t) (((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1);      if ((format & ESD_MASK_BITS) == ESD_BITS16)          ss->format = swap_bytes ? PA_SAMPLE_S16RE : PA_SAMPLE_S16NE;      else @@ -334,7 +334,7 @@ static int format_native2esd(pa_sample_spec *ss) {  /*** esound commands ***/ -static int esd_proto_connect(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { +static int esd_proto_connect(connection *c, esd_proto_t request, const void *data, size_t length) {      uint32_t ekey;      int ok; @@ -377,7 +377,7 @@ static int esd_proto_connect(connection *c, PA_GCC_UNUSED esd_proto_t request, c      return 0;  } -static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { +static int esd_proto_stream_play(connection *c, esd_proto_t request, const void *data, size_t length) {      char name[ESD_NAME_MAX], *utf8_name;      int32_t format, rate;      pa_sample_spec ss; @@ -397,7 +397,7 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques      rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);      data = (const char*) data + sizeof(int32_t); -    ss.rate = rate; +    ss.rate = (uint32_t) rate;      format_esd2native(format, c->swap_byte_order, &ss);      CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification"); @@ -430,7 +430,7 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques      CHECK_VALIDITY(c->sink_input, "Failed to create sink input."); -    l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS); +    l = (size_t) ((double) pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);      c->input_memblockq = pa_memblockq_new(              0,              l, @@ -455,7 +455,7 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques      c->protocol->n_player++; -    pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq)); +    pa_atomic_store(&c->playback.missing, (int) pa_memblockq_missing(c->input_memblockq));      pa_sink_input_put(c->sink_input); @@ -482,7 +482,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi      rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);      data = (const char*) data + sizeof(int32_t); -    ss.rate = rate; +    ss.rate = (uint32_t) rate;      format_esd2native(format, c->swap_byte_order, &ss);      CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification."); @@ -561,7 +561,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi      return 0;  } -static int esd_proto_get_latency(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { +static int esd_proto_get_latency(connection *c, esd_proto_t request, const void *data, size_t length) {      pa_sink *sink;      int32_t latency; @@ -572,7 +572,7 @@ static int esd_proto_get_latency(connection *c, PA_GCC_UNUSED esd_proto_t reques      if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1)))          latency = 0;      else { -        double usec = pa_sink_get_latency(sink); +        double usec = (double) pa_sink_get_latency(sink);          latency = (int) ((usec*44100)/1000000);      } @@ -581,7 +581,7 @@ static int esd_proto_get_latency(connection *c, PA_GCC_UNUSED esd_proto_t reques      return 0;  } -static int esd_proto_server_info(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { +static int esd_proto_server_info(connection *c, esd_proto_t request, const void *data, size_t length) {      int32_t rate = 44100, format = ESD_STEREO|ESD_BITS16;      int32_t response;      pa_sink *sink; @@ -591,7 +591,7 @@ static int esd_proto_server_info(connection *c, PA_GCC_UNUSED esd_proto_t reques      pa_assert(length == sizeof(int32_t));      if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1))) { -        rate = sink->sample_spec.rate; +        rate = (int32_t) sink->sample_spec.rate;          format = format_native2esd(&sink->sample_spec);      } @@ -641,9 +641,9 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da          if (conn->sink_input) {              pa_cvolume volume = *pa_sink_input_get_volume(conn->sink_input); -            rate = conn->sink_input->sample_spec.rate; -            lvolume = (volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM; -            rvolume = (volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM; +            rate = (int32_t) conn->sink_input->sample_spec.rate; +            lvolume = (int32_t) ((volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM); +            rvolume = (int32_t) ((volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);              format = format_native2esd(&conn->sink_input->sample_spec);          } @@ -706,15 +706,15 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da              connection_write(c, name, ESD_NAME_MAX);              /* rate */ -            rate = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, ce->sample_spec.rate); +            rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ce->sample_spec.rate);              connection_write(c, &rate, sizeof(int32_t));              /* left */ -            lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM); +            lvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ((ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM));              connection_write(c, &lvolume, sizeof(int32_t));              /*right*/ -            rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM); +            rvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ((ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM));              connection_write(c, &rvolume, sizeof(int32_t));              /*format*/ @@ -736,7 +736,7 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da      return 0;  } -static int esd_proto_stream_pan(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { +static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *data, size_t length) {      int32_t ok;      uint32_t idx, lvolume, rvolume;      connection *conn; @@ -772,7 +772,7 @@ static int esd_proto_stream_pan(connection *c, PA_GCC_UNUSED esd_proto_t request      return 0;  } -static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { +static int esd_proto_sample_cache(connection *c, esd_proto_t request, const void *data, size_t length) {      pa_sample_spec ss;      int32_t format, rate, sc_length;      uint32_t idx; @@ -790,7 +790,7 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque      rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);      data = (const char*)data + sizeof(int32_t); -    ss.rate = rate; +    ss.rate = (uint32_t) rate;      format_esd2native(format, c->swap_byte_order, &ss);      CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification."); @@ -807,9 +807,9 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque      CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");      pa_assert(!c->scache.memchunk.memblock); -    c->scache.memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, sc_length); +    c->scache.memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) sc_length);      c->scache.memchunk.index = 0; -    c->scache.memchunk.length = sc_length; +    c->scache.memchunk.length = (size_t) sc_length;      c->scache.sample_spec = ss;      pa_assert(!c->scache.name);      c->scache.name = pa_xstrdup(name); @@ -824,7 +824,7 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque      return 0;  } -static int esd_proto_sample_get_id(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { +static int esd_proto_sample_get_id(connection *c, esd_proto_t request, const void *data, size_t length) {      int32_t ok;      uint32_t idx;      char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1]; @@ -840,7 +840,7 @@ static int esd_proto_sample_get_id(connection *c, PA_GCC_UNUSED esd_proto_t requ      ok = -1;      if ((idx = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID) -        ok = idx + 1; +        ok = (int32_t) idx + 1;      connection_write(c, &ok, sizeof(int32_t)); @@ -867,12 +867,12 @@ static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, con              if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1)))                  if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0) -                    ok = idx + 1; +                    ok = (int32_t) idx + 1;          } else {              pa_assert(request == ESD_PROTO_SAMPLE_FREE);              if (pa_scache_remove_item(c->protocol->core, name) >= 0) -                ok = idx + 1; +                ok = (int32_t) idx + 1;          }      } @@ -881,7 +881,7 @@ static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, con      return 0;  } -static int esd_proto_standby_or_resume(connection *c, PA_GCC_UNUSED esd_proto_t request, PA_GCC_UNUSED const void *data, PA_GCC_UNUSED size_t length) { +static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const void *data, size_t length) {      int32_t ok;      connection_assert_ref(c); @@ -919,7 +919,9 @@ static int do_read(connection *c) {              return -1;          } -        if ((c->read_data_length+= r) >= sizeof(c->request)) { +        c->read_data_length += (size_t) r; + +        if (c->read_data_length >= sizeof(c->request)) {              struct proto_handler *handler;              c->request = PA_MAYBE_INT32_SWAP(c->swap_byte_order, c->request); @@ -970,7 +972,8 @@ static int do_read(connection *c) {              return -1;          } -        if ((c->read_data_length += r) >= handler->data_length) { +        c->read_data_length += (size_t) r; +        if (c->read_data_length >= handler->data_length) {              size_t l = c->read_data_length;              pa_assert(handler->proc); @@ -1000,7 +1003,7 @@ static int do_read(connection *c) {              return -1;          } -        c->scache.memchunk.index += r; +        c->scache.memchunk.index += (size_t) r;          pa_assert(c->scache.memchunk.index <= c->scache.memchunk.length);          if (c->scache.memchunk.index == c->scache.memchunk.length) { @@ -1033,7 +1036,7 @@ static int do_read(connection *c) {  /*         pa_log("STREAMING_DATA"); */ -        if (!(l = pa_atomic_load(&c->playback.missing))) +        if (!(l = (size_t) pa_atomic_load(&c->playback.missing)))              return 0;          if (c->playback.current_memblock) { @@ -1071,12 +1074,12 @@ static int do_read(connection *c) {          chunk.memblock = c->playback.current_memblock;          chunk.index = c->playback.memblock_index; -        chunk.length = r; +        chunk.length = (size_t) r; -        c->playback.memblock_index += r; +        c->playback.memblock_index += (size_t) r;          pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL); -        pa_atomic_sub(&c->playback.missing, r); +        pa_atomic_sub(&c->playback.missing, (int) r);      }      return 0; @@ -1100,7 +1103,8 @@ static int do_write(connection *c) {              return -1;          } -        if ((c->write_data_index +=r) >= c->write_data_length) +        c->write_data_index += (size_t) r; +        if (c->write_data_index >= c->write_data_length)              c->write_data_length = c->write_data_index = 0;      } else if (c->state == ESD_STREAMING_DATA && c->source_output) { @@ -1129,7 +1133,7 @@ static int do_write(connection *c) {              return -1;          } -        pa_memblockq_drop(c->output_memblockq, r); +        pa_memblockq_drop(c->output_memblockq, (size_t) r);      }      return 0; @@ -1288,7 +1292,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk          m = pa_memblockq_pop_missing(c->input_memblockq);          if (m > 0) -            if (pa_atomic_add(&c->playback.missing, m) <= 0) +            if (pa_atomic_add(&c->playback.missing, (int) m) <= 0)                  pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);          return 0; @@ -1574,7 +1578,7 @@ int pa_esound_options_parse(pa_esound_options *o, pa_core *c, pa_modargs *ma) {      if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {          pa_ip_acl *ipa; -        if (!(o->auth_ip_acl = pa_ip_acl_new(acl))) { +        if (!(ipa = pa_ip_acl_new(acl))) {              pa_log("Failed to parse IP ACL '%s'", acl);              return -1;          } diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index b9f6f083..56e86cb4 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -469,51 +469,102 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i      return 0;  } -static void fix_record_buffer_attr_pre(record_stream *s, pa_bool_t adjust_latency, uint32_t *maxlength, uint32_t *fragsize) { +static void fix_record_buffer_attr_pre( +        record_stream *s, +        pa_bool_t adjust_latency, +        pa_bool_t early_requests, +        uint32_t *maxlength, +        uint32_t *fragsize) { + +    size_t frame_size; +    pa_usec_t orig_fragsize_usec, fragsize_usec, source_usec; +      pa_assert(s);      pa_assert(maxlength);      pa_assert(fragsize); +    frame_size = pa_frame_size(&s->source_output->sample_spec); +      if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH)          *maxlength = MAX_MEMBLOCKQ_LENGTH;      if (*maxlength <= 0) -        *maxlength = pa_frame_size(&s->source_output->sample_spec); +        *maxlength = (uint32_t) frame_size;      if (*fragsize == (uint32_t) -1) -        *fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec); +        *fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);      if (*fragsize <= 0) -        *fragsize = pa_frame_size(&s->source_output->sample_spec); +        *fragsize = (uint32_t) frame_size; + +    orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec); -    if (adjust_latency) { -        pa_usec_t fragsize_usec; +    if (early_requests) { + +        /* In early request mode we need to emulate the classic +         * fragment-based playback model. We do this setting the source +         * latency to the fragment size. */ + +        source_usec = fragsize_usec; + +    } else if (adjust_latency) {          /* So, the user asked us to adjust the latency according to           * what the source can provide. Half the latency will be           * spent on the hw buffer, half of it in the async buffer           * queue we maintain for each client. */ -        fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec); +        source_usec = fragsize_usec/2; + +    } else { + +        /* Ok, the user didn't ask us to adjust the latency, hence we +         * don't */ -        s->source_latency = pa_source_output_set_requested_latency(s->source_output, fragsize_usec/2); +        source_usec = 0; +    } + +    if (source_usec > 0) +        s->source_latency = pa_source_output_set_requested_latency(s->source_output, source_usec); +    else +        s->source_latency = 0; + +    if (early_requests) { + +        /* Ok, we didn't necessarily get what we were asking for, so +         * let's tell the user */ + +        fragsize_usec = s->source_latency; + +    } else if (adjust_latency) { + +        /* Now subtract what we actually got */          if (fragsize_usec >= s->source_latency*2)              fragsize_usec -= s->source_latency;          else              fragsize_usec = s->source_latency; +    } -        *fragsize = pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec); -    } else -        s->source_latency = 0; +    if (pa_usec_to_bytes(orig_fragsize_usec, &s->source_output->sample_spec) != +        pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec)) + +        *fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec); + +    if (*fragsize <= 0) +        *fragsize = (uint32_t) frame_size;  } -static void fix_record_buffer_attr_post(record_stream *s, uint32_t *maxlength, uint32_t *fragsize) { +static void fix_record_buffer_attr_post( +        record_stream *s, +        uint32_t *maxlength, +        uint32_t *fragsize) { +      size_t base;      pa_assert(s);      pa_assert(maxlength);      pa_assert(fragsize); -    *maxlength = pa_memblockq_get_maxlength(s->memblockq); +    *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);      base = pa_frame_size(&s->source_output->sample_spec); @@ -524,7 +575,7 @@ static void fix_record_buffer_attr_post(record_stream *s, uint32_t *maxlength, u      if (s->fragment_size > *maxlength)          s->fragment_size = *maxlength; -    *fragsize = s->fragment_size; +    *fragsize = (uint32_t) s->fragment_size;  }  static record_stream* record_stream_new( @@ -538,7 +589,8 @@ static record_stream* record_stream_new(          pa_source_output_flags_t flags,          pa_proplist *p,          pa_bool_t adjust_latency, -        pa_sink_input *direct_on_input) { +        pa_sink_input *direct_on_input, +        pa_bool_t early_requests) {      record_stream *s;      pa_source_output *source_output; @@ -584,7 +636,7 @@ static record_stream* record_stream_new(      s->source_output->suspend = source_output_suspend_cb;      s->source_output->userdata = s; -    fix_record_buffer_attr_pre(s, adjust_latency, maxlength, fragsize); +    fix_record_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, fragsize);      s->memblockq = pa_memblockq_new(              0, @@ -666,10 +718,10 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,              uint32_t l = 0;              for (;;) { -                if ((l = pa_atomic_load(&s->missing)) <= 0) +                if ((l = (uint32_t) pa_atomic_load(&s->missing)) <= 0)                      break; -                if (pa_atomic_cmpxchg(&s->missing, l, 0)) +                if (pa_atomic_cmpxchg(&s->missing, (int) l, 0))                      break;              } @@ -690,6 +742,8 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,          case PLAYBACK_STREAM_MESSAGE_UNDERFLOW: {              pa_tagstruct *t; +/*             pa_log("signalling underflow"); */ +              /* Report that we're empty */              t = pa_tagstruct_new(NULL, 0);              pa_tagstruct_putu32(t, PA_COMMAND_UNDERFLOW); @@ -734,9 +788,17 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,      return 0;  } -static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_latency, uint32_t *maxlength, uint32_t *tlength, uint32_t* prebuf, uint32_t* minreq) { +static void fix_playback_buffer_attr_pre( +        playback_stream *s, +        pa_bool_t adjust_latency, +        pa_bool_t early_requests, +        uint32_t *maxlength, +        uint32_t *tlength, +        uint32_t* prebuf, +        uint32_t* minreq) { +      size_t frame_size; -    pa_usec_t tlength_usec, minreq_usec, sink_usec; +    pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec;      pa_assert(s);      pa_assert(maxlength); @@ -749,29 +811,39 @@ static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_la      if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH)          *maxlength = MAX_MEMBLOCKQ_LENGTH;      if (*maxlength <= 0) -        *maxlength = frame_size; +        *maxlength = (uint32_t) frame_size;      if (*tlength == (uint32_t) -1) -        *tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec); +        *tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);      if (*tlength <= 0) -        *tlength = frame_size; +        *tlength = (uint32_t) frame_size;      if (*minreq == (uint32_t) -1) -        *minreq = pa_usec_to_bytes(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec); +        *minreq = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);      if (*minreq <= 0) -        *minreq = frame_size; +        *minreq = (uint32_t) frame_size;      if (*tlength < *minreq+frame_size) -        *tlength = *minreq+frame_size; +        *tlength = *minreq+(uint32_t) frame_size; -    tlength_usec = pa_bytes_to_usec(*tlength, &s->sink_input->sample_spec); -    minreq_usec = pa_bytes_to_usec(*minreq, &s->sink_input->sample_spec); +    orig_tlength_usec = tlength_usec = pa_bytes_to_usec(*tlength, &s->sink_input->sample_spec); +    orig_minreq_usec = minreq_usec = pa_bytes_to_usec(*minreq, &s->sink_input->sample_spec);      pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms",                  (double) tlength_usec / PA_USEC_PER_MSEC,                  (double) minreq_usec / PA_USEC_PER_MSEC); -    if (adjust_latency) { +    if (early_requests) { + +        /* In early request mode we need to emulate the classic +         * fragment-based playback model. We do this setting the sink +         * latency to the fragment size. */ + +        sink_usec = minreq_usec; + +        pa_log_debug("Early requests mode enabled, configuring sink latency to minreq."); + +    } else if (adjust_latency) {          /* So, the user asked us to adjust the latency of the stream           * buffer according to the what the sink can provide. The @@ -795,6 +867,8 @@ static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_la          else              sink_usec = 0; +        pa_log_debug("Adjust latency mode enabled, configuring sink latency to half of overall latency."); +      } else {          /* Ok, the user didn't ask us to adjust the latency, but we @@ -805,11 +879,21 @@ static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_la              sink_usec = (tlength_usec - minreq_usec*2);          else              sink_usec = 0; + +        pa_log_debug("Traditional mode enabled, modifying sink usec only for compat with minreq.");      }      s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec); -    if (adjust_latency) { +    if (early_requests) { + +        /* Ok, we didn't necessarily get what we were asking for, so +         * let's tell the user */ + +        minreq_usec = s->sink_latency; + +    } else if (adjust_latency) { +          /* Ok, we didn't necessarily get what we were asking for, so           * let's subtract from what we asked for for the remaining           * buffer space */ @@ -823,22 +907,33 @@ static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_la      if (tlength_usec < s->sink_latency + 2*minreq_usec)          tlength_usec = s->sink_latency + 2*minreq_usec; -    *tlength = pa_usec_to_bytes(tlength_usec, &s->sink_input->sample_spec); -    *minreq = pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec); +    if (pa_usec_to_bytes_round_up(orig_tlength_usec, &s->sink_input->sample_spec) != +        pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec)) +        *tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec); + +    if (pa_usec_to_bytes(orig_minreq_usec, &s->sink_input->sample_spec) != +        pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec)) +        *minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);      if (*minreq <= 0) { -        *minreq += frame_size; -        *tlength += frame_size*2; +        *minreq = (uint32_t) frame_size; +        *tlength += (uint32_t) frame_size*2;      }      if (*tlength <= *minreq) -        *tlength =  *minreq*2 + frame_size; +        *tlength = *minreq*2 + (uint32_t) frame_size;      if (*prebuf == (uint32_t) -1 || *prebuf > *tlength)          *prebuf = *tlength;  } -static void fix_playback_buffer_attr_post(playback_stream *s, uint32_t *maxlength, uint32_t *tlength, uint32_t* prebuf, uint32_t* minreq) { +static void fix_playback_buffer_attr_post( +        playback_stream *s, +        uint32_t *maxlength, +        uint32_t *tlength, +        uint32_t* prebuf, +        uint32_t* minreq) { +      pa_assert(s);      pa_assert(maxlength);      pa_assert(tlength); @@ -864,11 +959,13 @@ static playback_stream* playback_stream_new(          uint32_t *minreq,          pa_cvolume *volume,          pa_bool_t muted, +        pa_bool_t muted_set,          uint32_t syncid,          uint32_t *missing,          pa_sink_input_flags_t flags,          pa_proplist *p, -        pa_bool_t adjust_latency) { +        pa_bool_t adjust_latency, +        pa_bool_t early_requests) {      playback_stream *s, *ssync;      pa_sink_input *sink_input; @@ -883,7 +980,6 @@ static playback_stream* playback_stream_new(      pa_assert(tlength);      pa_assert(prebuf);      pa_assert(minreq); -    pa_assert(volume);      pa_assert(missing);      pa_assert(p); @@ -916,8 +1012,10 @@ static playback_stream* playback_stream_new(      data.sink = sink;      pa_sink_input_new_data_set_sample_spec(&data, ss);      pa_sink_input_new_data_set_channel_map(&data, map); -    pa_sink_input_new_data_set_volume(&data, volume); -    pa_sink_input_new_data_set_muted(&data, muted); +    if (volume) +        pa_sink_input_new_data_set_volume(&data, volume); +    if (muted_set) +        pa_sink_input_new_data_set_muted(&data, muted);      data.sync_base = ssync ? ssync->sink_input : NULL;      sink_input = pa_sink_input_new(c->protocol->core, &data, flags); @@ -949,7 +1047,7 @@ static playback_stream* playback_stream_new(      start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0; -    fix_playback_buffer_attr_pre(s, adjust_latency, maxlength, tlength, prebuf, minreq); +    fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, tlength, prebuf, minreq);      pa_sink_input_get_silence(sink_input, &silence);      s->memblockq = pa_memblockq_new( @@ -982,7 +1080,6 @@ static playback_stream* playback_stream_new(      return s;  } -  /* Called from thread context */  static void playback_stream_request_bytes(playback_stream *s) {      size_t m, previous_missing; @@ -996,7 +1093,7 @@ static void playback_stream_request_bytes(playback_stream *s) {  /*     pa_log("request_bytes(%lu)", (unsigned long) m); */ -    previous_missing = pa_atomic_add(&s->missing, m); +    previous_missing = (size_t) pa_atomic_add(&s->missing, (int) m);      if (pa_memblockq_prebuf_active(s->memblockq) ||          (previous_missing < s->minreq && previous_missing+m >= s->minreq)) @@ -1142,7 +1239,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) {              pa_log_debug("Requesting rewind due to end of underrun.");              pa_sink_input_request_rewind(s->sink_input, -                                         s->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : s->sink_input->thread_info.underrun_for, +                                         (size_t) (s->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : s->sink_input->thread_info.underrun_for),                                           FALSE, TRUE);          } @@ -1156,7 +1253,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) {               * let's have it usk us again */              pa_log_debug("Requesting rewind due to rewrite."); -            pa_sink_input_request_rewind(s->sink_input, indexr - indexw, TRUE, FALSE); +            pa_sink_input_request_rewind(s->sink_input, (size_t) (indexr - indexw), TRUE, FALSE);          }      } @@ -1196,7 +1293,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int              if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {                  pa_log_warn("Failed to push data into queue");                  pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL); -                pa_memblockq_seek(s->memblockq, chunk->length, PA_SEEK_RELATIVE); +                pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE);              }              handle_seek(s, windex); @@ -1426,7 +1523,7 @@ static void sink_input_moved_cb(pa_sink_input *i) {      prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);      minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq); -    fix_playback_buffer_attr_pre(s, TRUE, &maxlength, &tlength, &prebuf, &minreq); +    fix_playback_buffer_attr_pre(s, TRUE, FALSE, &maxlength, &tlength, &prebuf, &minreq);      pa_memblockq_set_maxlength(s->memblockq, maxlength);      pa_memblockq_set_tlength(s->memblockq, tlength);      pa_memblockq_set_prebuf(s->memblockq, prebuf); @@ -1525,7 +1622,7 @@ static void source_output_moved_cb(pa_source_output *o) {      fragsize = (uint32_t) s->fragment_size;      maxlength = (uint32_t) pa_memblockq_get_length(s->memblockq); -    fix_record_buffer_attr_pre(s, TRUE, &maxlength, &fragsize); +    fix_record_buffer_attr_pre(s, TRUE, FALSE, &maxlength, &fragsize);      pa_memblockq_set_maxlength(s->memblockq, maxlength);      fix_record_buffer_attr_post(s, &maxlength, &fragsize); @@ -1572,7 +1669,7 @@ static pa_tagstruct *reply_new(uint32_t tag) {      return reply;  } -static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      playback_stream *s;      uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing; @@ -1592,10 +1689,14 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC          no_move = FALSE,          variable_rate = FALSE,          muted = FALSE, -        adjust_latency = FALSE; +        adjust_latency = FALSE, +        early_requests = FALSE, +        dont_inhibit_auto_suspend = FALSE, +        muted_set = FALSE;      pa_sink_input_flags_t flags = 0;      pa_proplist *p; +    pa_bool_t volume_set = TRUE;      pa_native_connection_assert_ref(c);      pa_assert(t); @@ -1621,7 +1722,9 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC      }      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); -    CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(sink_name)), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name(sink_name), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);      CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);      CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);      CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID); @@ -1660,6 +1763,26 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC          }      } +    if (c->version >= 14) { + +        if (pa_tagstruct_get_boolean(t, &volume_set) < 0 || +            pa_tagstruct_get_boolean(t, &early_requests) < 0) { +            protocol_error(c); +            pa_proplist_free(p); +            return; +        } +    } + +    if (c->version >= 15) { + +        if (pa_tagstruct_get_boolean(t, &muted_set) < 0 || +            pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0) { +            protocol_error(c); +            pa_proplist_free(p); +            return; +        } +    } +      if (!pa_tagstruct_eof(t)) {          protocol_error(c);          pa_proplist_free(p); @@ -1691,9 +1814,14 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC          (fix_rate ?  PA_SINK_INPUT_FIX_RATE : 0) |          (fix_channels ?  PA_SINK_INPUT_FIX_CHANNELS : 0) |          (no_move ?  PA_SINK_INPUT_DONT_MOVE : 0) | -        (variable_rate ?  PA_SINK_INPUT_VARIABLE_RATE : 0); +        (variable_rate ?  PA_SINK_INPUT_VARIABLE_RATE : 0) | +        (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0); + +    /* Only since protocol version 15 there's a seperate muted_set +     * flag. For older versions we synthesize it here */ +    muted_set = muted_set || muted; -    s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, &volume, muted, syncid, &missing, flags, p, adjust_latency); +    s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests);      pa_proplist_free(p);      CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); @@ -1735,7 +1863,7 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC      pa_pstream_send_tagstruct(c->pstream, reply);  } -static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      uint32_t channel; @@ -1793,7 +1921,7 @@ static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma      pa_pstream_send_simple_ack(c->pstream, tag);  } -static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      record_stream *s;      uint32_t maxlength, fragment_size; @@ -1813,7 +1941,9 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_          no_move = FALSE,          variable_rate = FALSE,          adjust_latency = FALSE, -        peak_detect = FALSE; +        peak_detect = FALSE, +        early_requests = FALSE, +        dont_inhibit_auto_suspend = FALSE;      pa_source_output_flags_t flags = 0;      pa_proplist *p;      uint32_t direct_on_input_idx = PA_INVALID_INDEX; @@ -1835,9 +1965,11 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_      }      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); +    CHECK_VALIDITY(c->pstream, !source_name || pa_namereg_is_valid_name(source_name), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, source_index == PA_INVALID_INDEX || !source_name, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !source_name || source_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);      CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);      CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID); -    CHECK_VALIDITY(c->pstream, source_index != PA_INVALID_INDEX || !source_name || (*source_name && pa_utf8_valid(source_name)), tag, PA_ERR_INVALID);      CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);      p = pa_proplist_new(); @@ -1874,6 +2006,24 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_          }      } +    if (c->version >= 14) { + +        if (pa_tagstruct_get_boolean(t, &early_requests) < 0) { +            protocol_error(c); +            pa_proplist_free(p); +            return; +        } +    } + +    if (c->version >= 15) { + +        if (pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0) { +            protocol_error(c); +            pa_proplist_free(p); +            return; +        } +    } +      if (!pa_tagstruct_eof(t)) {          protocol_error(c);          pa_proplist_free(p); @@ -1914,9 +2064,10 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_          (fix_rate ?  PA_SOURCE_OUTPUT_FIX_RATE : 0) |          (fix_channels ?  PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |          (no_move ?  PA_SOURCE_OUTPUT_DONT_MOVE : 0) | -        (variable_rate ?  PA_SOURCE_OUTPUT_VARIABLE_RATE : 0); +        (variable_rate ?  PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) | +        (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0); -    s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input); +    s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests);      pa_proplist_free(p);      CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); @@ -1953,7 +2104,7 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_      pa_pstream_send_tagstruct(c->pstream, reply);  } -static void command_exit(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      int ret; @@ -1972,11 +2123,11 @@ static void command_exit(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t      pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */  } -static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      const void*cookie;      pa_tagstruct *reply; -    pa_bool_t shm_on_remote, do_shm; +    pa_bool_t shm_on_remote = FALSE, do_shm;      pa_native_connection_assert_ref(c);      pa_assert(t); @@ -2071,6 +2222,7 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t          if (c->version < 10 || (c->version >= 13 && !shm_on_remote))              do_shm = FALSE; +#ifdef HAVE_CREDS      if (do_shm) {          /* Only enable SHM if both sides are owned by the same           * user. This is a security measure because otherwise data @@ -2080,6 +2232,7 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t          if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)              do_shm = FALSE;      } +#endif      pa_log_debug("Negotiated SHM: %s", pa_yes_no(do_shm));      pa_pstream_enable_shm(c->pstream, do_shm); @@ -2103,7 +2256,7 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t  #endif  } -static void command_set_client_name(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      const char *name = NULL;      pa_proplist *p; @@ -2143,7 +2296,7 @@ static void command_set_client_name(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE      pa_pstream_send_tagstruct(c->pstream, reply);  } -static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      const char *name;      uint32_t idx = PA_IDXSET_INVALID; @@ -2158,7 +2311,7 @@ static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uin      }      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); -    CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);      if (command == PA_COMMAND_LOOKUP_SINK) {          pa_sink *sink; @@ -2181,7 +2334,7 @@ static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uin      }  } -static void command_drain_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_drain_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      uint32_t idx;      playback_stream *s; @@ -2203,7 +2356,7 @@ static void command_drain_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC      pa_asyncmsgq_post(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_DRAIN, PA_UINT_TO_PTR(tag), 0, NULL, NULL);  } -static void command_stat(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_stat(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      pa_tagstruct *reply;      const pa_mempool_stat *stat; @@ -2225,11 +2378,11 @@ static void command_stat(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t      pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->allocated_size));      pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->n_accumulated));      pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->accumulated_size)); -    pa_tagstruct_putu32(reply, pa_scache_total_size(c->protocol->core)); +    pa_tagstruct_putu32(reply, (uint32_t) pa_scache_total_size(c->protocol->core));      pa_pstream_send_tagstruct(c->pstream, reply);  } -static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      pa_tagstruct *reply;      playback_stream *s; @@ -2275,7 +2428,7 @@ static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_      pa_pstream_send_tagstruct(c->pstream, reply);  } -static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      pa_tagstruct *reply;      record_stream *s; @@ -2307,7 +2460,7 @@ static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN      pa_pstream_send_tagstruct(c->pstream, reply);  } -static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      upload_stream *s;      uint32_t length; @@ -2349,7 +2502,7 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_          if (!(name = pa_proplist_gets(p, PA_PROP_EVENT_ID)))              name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME); -    CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);      s = upload_stream_new(c, &ss, &map, name, length, p);      pa_proplist_free(p); @@ -2362,7 +2515,7 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_      pa_pstream_send_tagstruct(c->pstream, reply);  } -static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_finish_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      uint32_t channel;      upload_stream *s; @@ -2391,7 +2544,7 @@ static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_      upload_stream_unlink(s);  } -static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      uint32_t sink_index;      pa_volume_t volume; @@ -2414,8 +2567,10 @@ static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui          return;      } -    CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(name)), tag, PA_ERR_INVALID); -    CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name(sink_name), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);      if (sink_index != PA_INVALID_INDEX)          sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index); @@ -2451,7 +2606,7 @@ static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui      pa_pstream_send_tagstruct(c->pstream, reply);  } -static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_remove_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      const char *name; @@ -2465,7 +2620,7 @@ static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED      }      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); -    CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);      if (pa_scache_remove_item(c->protocol->core, name) < 0) {          pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); @@ -2509,8 +2664,8 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin          PA_TAG_SAMPLE_SPEC, &fixed_ss,          PA_TAG_CHANNEL_MAP, &sink->channel_map,          PA_TAG_U32, sink->module ? sink->module->index : PA_INVALID_INDEX, -        PA_TAG_CVOLUME, pa_sink_get_volume(sink), -        PA_TAG_BOOLEAN, pa_sink_get_mute(sink), +        PA_TAG_CVOLUME, pa_sink_get_volume(sink, FALSE), +        PA_TAG_BOOLEAN, pa_sink_get_mute(sink, FALSE),          PA_TAG_U32, sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,          PA_TAG_STRING, sink->monitor_source ? sink->monitor_source->name : NULL,          PA_TAG_USEC, pa_sink_get_latency(sink), @@ -2540,8 +2695,8 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s          PA_TAG_SAMPLE_SPEC, &fixed_ss,          PA_TAG_CHANNEL_MAP, &source->channel_map,          PA_TAG_U32, source->module ? source->module->index : PA_INVALID_INDEX, -        PA_TAG_CVOLUME, pa_source_get_volume(source), -        PA_TAG_BOOLEAN, pa_source_get_mute(source), +        PA_TAG_CVOLUME, pa_source_get_volume(source, FALSE), +        PA_TAG_BOOLEAN, pa_source_get_mute(source, FALSE),          PA_TAG_U32, source->monitor_of ? source->monitor_of->index : PA_INVALID_INDEX,          PA_TAG_STRING, source->monitor_of ? source->monitor_of->name : NULL,          PA_TAG_USEC, pa_source_get_latency(source), @@ -2577,7 +2732,7 @@ static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {      pa_tagstruct_putu32(t, module->index);      pa_tagstruct_puts(t, module->name);      pa_tagstruct_puts(t, module->argument); -    pa_tagstruct_putu32(t, module->n_used); +    pa_tagstruct_putu32(t, (uint32_t) module->n_used);      pa_tagstruct_put_boolean(t, module->auto_unload);  } @@ -2597,7 +2752,7 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t,      pa_tagstruct_putu32(t, s->sink->index);      pa_tagstruct_put_sample_spec(t, &fixed_ss);      pa_tagstruct_put_channel_map(t, &s->channel_map); -    pa_tagstruct_put_cvolume(t, &s->volume); +    pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s));      pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency));      pa_tagstruct_put_usec(t, sink_latency);      pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s))); @@ -2650,7 +2805,7 @@ static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s      pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0);      pa_tagstruct_put_sample_spec(t, &fixed_ss);      pa_tagstruct_put_channel_map(t, &e->channel_map); -    pa_tagstruct_putu32(t, e->memchunk.length); +    pa_tagstruct_putu32(t, (uint32_t) e->memchunk.length);      pa_tagstruct_put_boolean(t, e->lazy);      pa_tagstruct_puts(t, e->filename); @@ -2658,7 +2813,7 @@ static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s          pa_tagstruct_put_proplist(t, e->proplist);  } -static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      uint32_t idx;      pa_sink *sink = NULL; @@ -2668,7 +2823,7 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u      pa_sink_input *si = NULL;      pa_source_output *so = NULL;      pa_scache_entry *sce = NULL; -    const char *name; +    const char *name = NULL;      pa_tagstruct *reply;      pa_native_connection_assert_ref(c); @@ -2686,7 +2841,10 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u      }      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); -    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);      if (command == PA_COMMAND_GET_SINK_INFO) {          if (idx != PA_INVALID_INDEX) @@ -2737,7 +2895,7 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u      pa_pstream_send_tagstruct(c->pstream, reply);  } -static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      pa_idxset *i;      uint32_t idx; @@ -2797,7 +2955,7 @@ static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma      pa_pstream_send_tagstruct(c->pstream, reply);  } -static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      pa_tagstruct *reply;      char txt[256]; @@ -2847,7 +3005,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint3      pa_pstream_send_tagstruct(c->pstream, t);  } -static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_subscribe(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      pa_subscription_mask_t m; @@ -2876,7 +3034,7 @@ static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint  }  static void command_set_volume( -        PA_GCC_UNUSED pa_pdispatch *pd, +        pa_pdispatch *pd,          uint32_t command,          uint32_t tag,          pa_tagstruct *t, @@ -2903,7 +3061,10 @@ static void command_set_volume(      }      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); -    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);      CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);      switch (command) { @@ -2943,7 +3104,7 @@ static void command_set_volume(  }  static void command_set_mute( -        PA_GCC_UNUSED pa_pdispatch *pd, +        pa_pdispatch *pd,          uint32_t command,          uint32_t tag,          pa_tagstruct *t, @@ -2970,7 +3131,10 @@ static void command_set_mute(      }      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); -    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);      switch (command) { @@ -3011,7 +3175,7 @@ static void command_set_mute(      pa_pstream_send_simple_ack(c->pstream, tag);  } -static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_cork_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      uint32_t idx;      pa_bool_t b; @@ -3034,10 +3198,14 @@ static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_      CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);      pa_sink_input_cork(s->sink_input, b); + +    if (b) +        s->is_underrun = TRUE; +      pa_pstream_send_simple_ack(c->pstream, tag);  } -static void command_trigger_or_flush_or_prebuf_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_trigger_or_flush_or_prebuf_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      uint32_t idx;      playback_stream *s; @@ -3077,7 +3245,7 @@ static void command_trigger_or_flush_or_prebuf_playback_stream(PA_GCC_UNUSED pa_      pa_pstream_send_simple_ack(c->pstream, tag);  } -static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      uint32_t idx;      record_stream *s; @@ -3102,7 +3270,7 @@ static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN      pa_pstream_send_simple_ack(c->pstream, tag);  } -static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      uint32_t idx;      record_stream *s; @@ -3142,7 +3310,7 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u      if (command == PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR) {          playback_stream *s; -        pa_bool_t adjust_latency = FALSE; +        pa_bool_t adjust_latency = FALSE, early_requests = FALSE;          s = pa_idxset_get_by_index(c->output_streams, idx);          CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); @@ -3156,12 +3324,13 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u                      PA_TAG_U32, &minreq,                      PA_TAG_INVALID) < 0 ||              (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) || +            (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||              !pa_tagstruct_eof(t)) {              protocol_error(c);              return;          } -        fix_playback_buffer_attr_pre(s, adjust_latency, &maxlength, &tlength, &prebuf, &minreq); +        fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &tlength, &prebuf, &minreq);          pa_memblockq_set_maxlength(s->memblockq, maxlength);          pa_memblockq_set_tlength(s->memblockq, tlength);          pa_memblockq_set_prebuf(s->memblockq, prebuf); @@ -3179,7 +3348,7 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u      } else {          record_stream *s; -        pa_bool_t adjust_latency = FALSE; +        pa_bool_t adjust_latency = FALSE, early_requests = FALSE;          pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR);          s = pa_idxset_get_by_index(c->record_streams, idx); @@ -3191,12 +3360,13 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u                      PA_TAG_U32, &fragsize,                      PA_TAG_INVALID) < 0 ||              (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) || +            (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||              !pa_tagstruct_eof(t)) {              protocol_error(c);              return;          } -        fix_record_buffer_attr_pre(s, adjust_latency, &maxlength, &fragsize); +        fix_record_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &fragsize);          pa_memblockq_set_maxlength(s->memblockq, maxlength);          fix_record_buffer_attr_post(s, &maxlength, &fragsize); @@ -3387,7 +3557,7 @@ static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t          if (!z)              break; -        changed += pa_proplist_unset(p, z) >= 0; +        changed += (unsigned) (pa_proplist_unset(p, z) >= 0);          pa_xfree(z);      } @@ -3413,7 +3583,7 @@ static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t      }  } -static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      const char *s; @@ -3427,13 +3597,13 @@ static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, u      }      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); -    CHECK_VALIDITY(c->pstream, !s || (*s && pa_utf8_valid(s)), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !s || pa_namereg_is_valid_name(s), tag, PA_ERR_INVALID);      pa_namereg_set_default(c->protocol->core, s, command == PA_COMMAND_SET_DEFAULT_SOURCE ? PA_NAMEREG_SOURCE : PA_NAMEREG_SINK);      pa_pstream_send_simple_ack(c->pstream, tag);  } -static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      uint32_t idx;      const char *name; @@ -3473,7 +3643,7 @@ static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t com      pa_pstream_send_simple_ack(c->pstream, tag);  } -static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      uint32_t idx; @@ -3521,7 +3691,7 @@ static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint3      pa_native_connection_unref(c);  } -static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_load_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      pa_module *m;      const char *name, *argument; @@ -3551,7 +3721,7 @@ static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui      pa_pstream_send_tagstruct(c->pstream, reply);  } -static void command_unload_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      uint32_t idx;      pa_module *m; @@ -3573,7 +3743,7 @@ static void command_unload_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED      pa_pstream_send_simple_ack(c->pstream, tag);  } -static void command_add_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_add_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      const char *name, *module, *argument;      uint32_t type; @@ -3608,7 +3778,7 @@ static void command_add_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED u      pa_pstream_send_tagstruct(c->pstream, reply);  } -static void command_remove_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_remove_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      const char *name = NULL;      uint32_t type, idx = PA_IDXSET_INVALID; @@ -3644,12 +3814,12 @@ static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e)      pa_tagstruct_putu32(t, e->index);      pa_tagstruct_puts(t, e->name); -    pa_tagstruct_putu32(t, e->type == PA_NAMEREG_SINK ? 0 : 1); +    pa_tagstruct_putu32(t, e->type == PA_NAMEREG_SINK ? 0U : 1U);      pa_tagstruct_puts(t, e->module);      pa_tagstruct_puts(t, e->argument);  } -static void command_get_autoload_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_get_autoload_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      const pa_autoload_entry *a = NULL;      uint32_t type, idx; @@ -3683,7 +3853,7 @@ static void command_get_autoload_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNU      pa_pstream_send_tagstruct(c->pstream, reply);  } -static void command_get_autoload_info_list(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      pa_tagstruct *reply; @@ -3713,14 +3883,14 @@ static void command_get_autoload_info_list(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC  static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {      pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);      uint32_t idx = PA_INVALID_INDEX, idx_device = PA_INVALID_INDEX; -    const char *name = NULL; +    const char *name_device = NULL;      pa_native_connection_assert_ref(c);      pa_assert(t);      if (pa_tagstruct_getu32(t, &idx) < 0 ||          pa_tagstruct_getu32(t, &idx_device) < 0 || -        pa_tagstruct_gets(t, &name) < 0 || +        pa_tagstruct_gets(t, &name_device) < 0 ||          !pa_tagstruct_eof(t)) {          protocol_error(c);          return; @@ -3728,7 +3898,11 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);      CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID); -    CHECK_VALIDITY(c->pstream, idx_device != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID); + +    CHECK_VALIDITY(c->pstream, !name_device || pa_namereg_is_valid_name(name_device), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, idx_device != PA_INVALID_INDEX || name_device, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, idx_device == PA_INVALID_INDEX || !name_device, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !name_device || idx_device == PA_INVALID_INDEX, tag, PA_ERR_INVALID);      if (command == PA_COMMAND_MOVE_SINK_INPUT) {          pa_sink_input *si = NULL; @@ -3739,7 +3913,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag          if (idx_device != PA_INVALID_INDEX)              sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx_device);          else -            sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1); +            sink = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SINK, 1);          CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY); @@ -3758,7 +3932,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag          if (idx_device != PA_INVALID_INDEX)              source = pa_idxset_get_by_index(c->protocol->core->sources, idx_device);          else -            source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1); +            source = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SOURCE, 1);          CHECK_VALIDITY(c->pstream, so && source, tag, PA_ERR_NOENTITY); @@ -3789,7 +3963,10 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa      }      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); -    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || !*name || pa_utf8_valid(name), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);      if (command == PA_COMMAND_SUSPEND_SINK) { @@ -3862,7 +4039,10 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag,      }      CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); -    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || !*name || pa_utf8_valid(name), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !name || pa_utf8_valid(name), tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID); +    CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);      if (idx != PA_INVALID_INDEX)          m = pa_idxset_get_by_index(c->protocol->core->modules, idx); @@ -3912,6 +4092,8 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o          return;      } +/*     pa_log("got %lu bytes", (unsigned long) chunk->length); */ +      if (playback_stream_isinstance(stream)) {          playback_stream *ps = PLAYBACK_STREAM(stream); @@ -4299,7 +4481,7 @@ int pa_native_options_parse(pa_native_options *o, pa_core *c, pa_modargs *ma) {      if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {          pa_ip_acl *ipa; -        if (!(o->auth_ip_acl = pa_ip_acl_new(acl))) { +        if (!(ipa = pa_ip_acl_new(acl))) {              pa_log("Failed to parse IP ACL '%s'", acl);              return -1;          } diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c index 78874bb9..743bf2ee 100644 --- a/src/pulsecore/protocol-simple.c +++ b/src/pulsecore/protocol-simple.c @@ -159,7 +159,7 @@ static int do_read(connection *c) {      connection_assert_ref(c); -    if (!c->sink_input || (l = pa_atomic_load(&c->playback.missing)) <= 0) +    if (!c->sink_input || (l = (size_t) pa_atomic_load(&c->playback.missing)) <= 0)          return 0;      if (c->playback.current_memblock) { @@ -173,7 +173,7 @@ static int do_read(connection *c) {      }      if (!c->playback.current_memblock) { -        pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, 0)); +        pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) -1));          c->playback.memblock_index = 0;          space = pa_memblock_get_length(c->playback.current_memblock); @@ -197,12 +197,12 @@ static int do_read(connection *c) {      chunk.memblock = c->playback.current_memblock;      chunk.index = c->playback.memblock_index; -    chunk.length = r; +    chunk.length = (size_t) r; -    c->playback.memblock_index += r; +    c->playback.memblock_index += (size_t) r;      pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL); -    pa_atomic_sub(&c->playback.missing, r); +    pa_atomic_sub(&c->playback.missing, (int) r);      return 0;  } @@ -240,7 +240,7 @@ static int do_write(connection *c) {          return -1;      } -    pa_memblockq_drop(c->output_memblockq, r); +    pa_memblockq_drop(c->output_memblockq, (size_t) r);      return 0;  } @@ -377,7 +377,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk          m = pa_memblockq_pop_missing(c->input_memblockq);          if (m > 0) -            if (pa_atomic_add(&c->playback.missing, m) <= 0) +            if (pa_atomic_add(&c->playback.missing, (int) m) <= 0)                  pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);          return 0; @@ -492,6 +492,8 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp      c->parent.parent.free = connection_free;      c->parent.process_msg = connection_process_msg;      c->io = io; +    pa_iochannel_set_callback(c->io, io_callback, c); +      c->sink_input = NULL;      c->source_output = NULL;      c->input_memblockq = c->output_memblockq = NULL; @@ -546,7 +548,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp          pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY); -        l = (size_t) (pa_bytes_per_second(&o->sample_spec)*PLAYBACK_BUFFER_SECONDS); +        l = (size_t) ((double) pa_bytes_per_second(&o->sample_spec)*PLAYBACK_BUFFER_SECONDS);          c->input_memblockq = pa_memblockq_new(                  0,                  l, @@ -558,7 +560,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp                  NULL);          pa_iochannel_socket_set_rcvbuf(io, l); -        pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq)); +        pa_atomic_store(&c->playback.missing, (int) pa_memblockq_missing(c->input_memblockq));          pa_sink_input_put(c->sink_input);      } @@ -610,7 +612,6 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp          pa_source_output_put(c->source_output);      } -    pa_iochannel_set_callback(c->io, io_callback, c);      pa_idxset_put(p->connections, c, NULL);      return; @@ -689,6 +690,9 @@ pa_simple_options* pa_simple_options_new(void) {      o = pa_xnew0(pa_simple_options, 1);      PA_REFCNT_INIT(o); +    o->record = FALSE; +    o->playback = TRUE; +      return o;  } @@ -733,14 +737,14 @@ int pa_simple_options_parse(pa_simple_options *o, pa_core *c, pa_modargs *ma) {      pa_xfree(o->default_sink);      o->default_sink = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); -    enabled = FALSE; +    enabled = o->record;      if (pa_modargs_get_value_boolean(ma, "record", &enabled) < 0) {          pa_log("record= expects a boolean argument.");          return -1;      }      o->record = enabled; -    enabled = TRUE; +    enabled = o->playback;      if (pa_modargs_get_value_boolean(ma, "playback", &enabled) < 0) {          pa_log("playback= expects a boolean argument.");          return -1; diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c index 6b1af67b..7ff8edc9 100644 --- a/src/pulsecore/pstream.c +++ b/src/pulsecore/pstream.c @@ -283,7 +283,7 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo      return p;  } -static void item_free(void *item, PA_GCC_UNUSED void *q) { +static void item_free(void *item, void *q) {      struct item_info *i = item;      pa_assert(i); @@ -488,7 +488,7 @@ static void prepare_next_write_item(pa_pstream *p) {          pa_assert(p->write.current->packet);          p->write.data = p->write.current->packet->data; -        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->packet->length); +        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl((uint32_t) p->write.current->packet->length);      } else if (p->write.current->type == PA_PSTREAM_ITEM_SHMRELEASE) { @@ -511,7 +511,7 @@ static void prepare_next_write_item(pa_pstream *p) {          p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl((uint32_t) (((uint64_t) p->write.current->offset) >> 32));          p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = htonl((uint32_t) ((uint64_t) p->write.current->offset)); -        flags = p->write.current->seek_mode & PA_FLAG_SEEKMASK; +        flags = (uint32_t) (p->write.current->seek_mode & PA_FLAG_SEEKMASK);          if (p->use_shm) {              uint32_t block_id, shm_id; @@ -542,7 +542,7 @@ static void prepare_next_write_item(pa_pstream *p) {          }          if (send_payload) { -            p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->chunk.length); +            p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl((uint32_t) p->write.current->chunk.length);              p->write.memchunk = p->write.current->chunk;              pa_memblock_ref(p->write.memchunk.memblock);              p->write.data = NULL; @@ -607,7 +607,7 @@ static int do_write(pa_pstream *p) {      if (release_memblock)          pa_memblock_release(release_memblock); -    p->write.index += r; +    p->write.index += (size_t) r;      if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE + ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) {          pa_assert(p->write.current); @@ -675,7 +675,7 @@ static int do_read(pa_pstream *p) {      if (release_memblock)          pa_memblock_release(release_memblock); -    p->read.index += r; +    p->read.index += (size_t) r;      if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) {          uint32_t flags, length, channel; @@ -769,7 +769,7 @@ static int do_read(pa_pstream *p) {          if (p->read.memblock && p->recieve_memblock_callback) {              /* Is this memblock data? Than pass it to the user */ -            l = (p->read.index - r) < PA_PSTREAM_DESCRIPTOR_SIZE ? p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE : (size_t) r; +            l = (p->read.index - (size_t) r) < PA_PSTREAM_DESCRIPTOR_SIZE ? (size_t) (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE) : (size_t) r;              if (l > 0) {                  pa_memchunk chunk; diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index c82f4c1e..b2d512c8 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -373,7 +373,7 @@ size_t pa_resampler_max_block_size(pa_resampler *r) {      /* We deduce the "largest" sample spec we're using during the       * conversion */ -    ss.channels = PA_MAX(r->i_ss.channels, r->o_ss.channels); +    ss.channels = (uint8_t) (PA_MAX(r->i_ss.channels, r->o_ss.channels));      /* We silently assume that the format enum is ordered by size */      ss.format = PA_MAX(r->i_ss.format, r->o_ss.format); @@ -642,7 +642,7 @@ static void calc_map_table(pa_resampler *r) {                  if (n > 0)                      for (ic = 0; ic < r->i_ss.channels; ic++)                          if (on_left(r->i_cm.map[ic])) { -                            r->map_table[oc][ic] = 1.0 / n; +                            r->map_table[oc][ic] = 1.0f / (float) n;                              ic_connected[ic] = TRUE;                          } @@ -663,7 +663,7 @@ static void calc_map_table(pa_resampler *r) {                  if (n > 0)                      for (ic = 0; ic < r->i_ss.channels; ic++)                          if (on_right(r->i_cm.map[ic])) { -                            r->map_table[oc][ic] = 1.0 / n; +                            r->map_table[oc][ic] = 1.0f / (float) n;                              ic_connected[ic] = TRUE;                          } @@ -684,7 +684,7 @@ static void calc_map_table(pa_resampler *r) {                  if (n > 0) {                      for (ic = 0; ic < r->i_ss.channels; ic++)                          if (on_center(r->i_cm.map[ic])) { -                            r->map_table[oc][ic] = 1.0 / n; +                            r->map_table[oc][ic] = 1.0f / (float) n;                              ic_connected[ic] = TRUE;                          }                  } else { @@ -701,7 +701,7 @@ static void calc_map_table(pa_resampler *r) {                      if (n > 0)                          for (ic = 0; ic < r->i_ss.channels; ic++)                              if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic])) { -                                r->map_table[oc][ic] = 1.0 / n; +                                r->map_table[oc][ic] = 1.0f / (float) n;                                  ic_connected[ic] = TRUE;                              } @@ -716,7 +716,11 @@ static void calc_map_table(pa_resampler *r) {                   * channels for LFE. */                  for (ic = 0; ic < r->i_ss.channels; ic++) { -                    r->map_table[oc][ic] = 1.0 / r->i_ss.channels; + +                    if (!(r->flags & PA_RESAMPLER_NO_LFE)) +                        r->map_table[oc][ic] = 1.0f / (float) r->i_ss.channels; +                    else +                        r->map_table[oc][ic] = 0;                      /* Please note that a channel connected to LFE                       * doesn't really count as connected. */ @@ -763,12 +767,12 @@ static void calc_map_table(pa_resampler *r) {                  for (ic = 0; ic < r->i_ss.channels; ic++) {                      if (ic_connected[ic]) { -                        r->map_table[oc][ic] *= .9; +                        r->map_table[oc][ic] *= .9f;                          continue;                      }                      if (on_left(r->i_cm.map[ic])) -                        r->map_table[oc][ic] = .1 / ic_unconnected_left; +                        r->map_table[oc][ic] = .1f / (float) ic_unconnected_left;                  }              }          } @@ -788,12 +792,12 @@ static void calc_map_table(pa_resampler *r) {                  for (ic = 0; ic < r->i_ss.channels; ic++) {                      if (ic_connected[ic]) { -                        r->map_table[oc][ic] *= .9; +                        r->map_table[oc][ic] *= .9f;                          continue;                      }                      if (on_right(r->i_cm.map[ic])) -                        r->map_table[oc][ic] = .1 / ic_unconnected_right; +                        r->map_table[oc][ic] = .1f / (float) ic_unconnected_right;                  }              }          } @@ -814,12 +818,12 @@ static void calc_map_table(pa_resampler *r) {                  for (ic = 0; ic < r->i_ss.channels; ic++)  {                      if (ic_connected[ic]) { -                        r->map_table[oc][ic] *= .9; +                        r->map_table[oc][ic] *= .9f;                          continue;                      }                      if (on_center(r->i_cm.map[ic])) { -                        r->map_table[oc][ic] = .1 / ic_unconnected_center; +                        r->map_table[oc][ic] = .1f / (float) ic_unconnected_center;                          mixed_in = TRUE;                      }                  } @@ -840,18 +844,18 @@ static void calc_map_table(pa_resampler *r) {                      for (ic = 0; ic < r->i_ss.channels; ic++)  {                          if (ic_connected[ic]) { -                            r->map_table[oc][ic] *= .75; +                            r->map_table[oc][ic] *= .75f;                              continue;                          }                          if (on_center(r->i_cm.map[ic])) -                            r->map_table[oc][ic] = .375 / ic_unconnected_center; +                            r->map_table[oc][ic] = .375f / (float) ic_unconnected_center;                      }                  }              }          } -        if (ic_unconnected_lfe > 0) { +        if (ic_unconnected_lfe > 0 && !(r->flags & PA_RESAMPLER_NO_LFE)) {              /* OK, so there is an unconnected LFE channel. Let's mix               * it into all channels, with factor 0.375 */ @@ -862,7 +866,7 @@ static void calc_map_table(pa_resampler *r) {                      continue;                  for (oc = 0; oc < r->o_ss.channels; oc++) -                    r->map_table[oc][ic] = 0.375 / ic_unconnected_lfe; +                    r->map_table[oc][ic] = 0.375f / (float) ic_unconnected_lfe;              }          }      } @@ -905,7 +909,7 @@ static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input)      if (!r->to_work_format_func || !input->length)          return input; -    n_samples = (input->length / r->i_fz) * r->i_ss.channels; +    n_samples = (unsigned) ((input->length / r->i_fz) * r->i_ss.channels);      r->buf1.index = 0;      r->buf1.length = r->w_sz * n_samples; @@ -974,7 +978,7 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {      if (!r->map_required || !input->length)          return input; -    in_n_samples = input->length / r->w_sz; +    in_n_samples = (unsigned) (input->length / r->w_sz);      n_frames = in_n_samples / r->i_ss.channels;      out_n_samples = n_frames * r->o_ss.channels; @@ -994,8 +998,8 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {      memset(dst, 0, r->buf2.length); -    o_skip = r->w_sz * r->o_ss.channels; -    i_skip = r->w_sz * r->i_ss.channels; +    o_skip = (int) (r->w_sz * r->o_ss.channels); +    i_skip = (int) (r->w_sz * r->i_ss.channels);      switch (r->work_format) {          case PA_SAMPLE_FLOAT32NE: @@ -1013,7 +1017,7 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {                              (float*) dst + oc, o_skip,                              (float*) dst + oc, o_skip,                              (float*) src + ic, i_skip, -                            n_frames, +                            (int) n_frames,                              &one, &r->map_table[oc][ic]);                  }              } @@ -1037,7 +1041,7 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {                                  (int16_t*) dst + oc, o_skip,                                  (int16_t*) dst + oc, o_skip,                                  (int16_t*) src + ic, i_skip, -                                n_frames, +                                (int) n_frames,                                  &one, &one);                      } else @@ -1046,8 +1050,8 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {                                  (int16_t*) dst + oc, o_skip,                                  (int16_t*) dst + oc, o_skip,                                  (int16_t*) src + ic, i_skip, -                                n_frames, -                                1.0, r->map_table[oc][ic]); +                                (int) n_frames, +                                1.0f, r->map_table[oc][ic]);                  }              } @@ -1077,8 +1081,8 @@ static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {      if (!r->impl_resample || !input->length)          return input; -    in_n_samples = input->length / r->w_sz; -    in_n_frames = in_n_samples / r->o_ss.channels; +    in_n_samples = (unsigned) (input->length / r->w_sz); +    in_n_frames = (unsigned) (in_n_samples / r->o_ss.channels);      out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_FRAMES;      out_n_samples = out_n_frames * r->o_ss.channels; @@ -1112,8 +1116,8 @@ static pa_memchunk *convert_from_work_format(pa_resampler *r, pa_memchunk *input      if (!r->from_work_format_func || !input->length)          return input; -    n_samples = input->length / r->w_sz; -    n_frames =  n_samples / r->o_ss.channels; +    n_samples = (unsigned) (input->length / r->w_sz); +    n_frames = n_samples / r->o_ss.channels;      r->buf4.index = 0;      r->buf4.length = r->o_fz * n_frames; @@ -1178,10 +1182,10 @@ static void libsamplerate_resample(pa_resampler *r, const pa_memchunk *input, un      memset(&data, 0, sizeof(data));      data.data_in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index); -    data.input_frames = in_n_frames; +    data.input_frames = (long int) in_n_frames;      data.data_out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index); -    data.output_frames = *out_n_frames; +    data.output_frames = (long int) *out_n_frames;      data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;      data.end_of_input = 0; @@ -1192,7 +1196,7 @@ static void libsamplerate_resample(pa_resampler *r, const pa_memchunk *input, un      pa_memblock_release(input->memblock);      pa_memblock_release(output->memblock); -    *out_n_frames = data.output_frames_gen; +    *out_n_frames = (unsigned) data.output_frames_gen;  }  static void libsamplerate_update_rates(pa_resampler *r) { @@ -1354,7 +1358,7 @@ static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned          pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));          oil_memcpy((uint8_t*) dst + fz * o_index, -                   (uint8_t*) src + fz * j, fz); +                   (uint8_t*) src + fz * j, (int) fz);      }      pa_memblock_release(input->memblock); @@ -1414,40 +1418,46 @@ static void peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned i          unsigned j;          j = ((r->peaks.o_counter * r->i_ss.rate) / r->o_ss.rate); -        j = j > r->peaks.i_counter ? j - r->peaks.i_counter : 0; -        if (j >= in_n_frames) -            break; +        if (j > r->peaks.i_counter) +            j -= r->peaks.i_counter; +        else +            j = 0;          pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));          if (r->work_format == PA_SAMPLE_S16NE) {              unsigned i, c; -            int16_t *s = (int16_t*) ((uint8_t*) src + fz * j); +            int16_t *s = (int16_t*) ((uint8_t*) src + fz * start);              int16_t *d = (int16_t*) ((uint8_t*) dst + fz * o_index); -            for (i = start; i <= j; i++) +            for (i = start; i <= j && i < in_n_frames; i++) +                  for (c = 0; c < r->o_ss.channels; c++, s++) {                      int16_t n; -                    n = *s < 0 ? -*s : *s; +                    n = (int16_t) (*s < 0 ? -*s : *s); -                    if (n > r->peaks.max_i[c]) +                    if (PA_UNLIKELY(n > r->peaks.max_i[c]))                          r->peaks.max_i[c] = n;                  } +            if (i >= in_n_frames) +                break; +              for (c = 0; c < r->o_ss.channels; c++, d++) { -                 *d = r->peaks.max_i[c]; -                 r->peaks.max_i[c] = 0; +                *d = r->peaks.max_i[c]; +                r->peaks.max_i[c] = 0;              } +          } else {              unsigned i, c; -            float *s = (float*) ((uint8_t*) src + fz * j); +            float *s = (float*) ((uint8_t*) src + fz * start);              float *d = (float*) ((uint8_t*) dst + fz * o_index);              pa_assert(r->work_format == PA_SAMPLE_FLOAT32NE); -            for (i = start; i <= j; i++) +            for (i = start; i <= j && i < in_n_frames; i++)                  for (c = 0; c < r->o_ss.channels; c++, s++) {                      float n = fabsf(*s); @@ -1455,13 +1465,16 @@ static void peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned i                          r->peaks.max_f[c] = n;                  } +            if (i >= in_n_frames) +                break; +              for (c = 0; c < r->o_ss.channels; c++, d++) {                  *d = r->peaks.max_f[c];                  r->peaks.max_f[c] = 0;              }          } -        start = j+1; +        start = j;      }      pa_memblock_release(input->memblock); @@ -1523,7 +1536,7 @@ static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned          p = pa_memblock_acquire(b);          /* Copy the remaining data into it */ -        l = r->ffmpeg.buf[c].length; +        l = (unsigned) r->ffmpeg.buf[c].length;          if (r->ffmpeg.buf[c].memblock) {              t = (int16_t*) ((uint8_t*) pa_memblock_acquire(r->ffmpeg.buf[c].memblock) + r->ffmpeg.buf[c].index);              memcpy(p, t, l); @@ -1543,18 +1556,18 @@ static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned          pa_memblock_release(input->memblock);          /* Calculate the resulting number of frames */ -        in = in_n_frames + l / sizeof(int16_t); +        in = (unsigned) in_n_frames + l / (unsigned) sizeof(int16_t);          /* Allocate buffer for the result */          w = pa_memblock_new(r->mempool, *out_n_frames * sizeof(int16_t));          q = pa_memblock_acquire(w);          /* Now, resample */ -        used_frames = av_resample(r->ffmpeg.state, -                                  q, p, -                                  &consumed_frames, -                                  in, *out_n_frames, -                                  c >= (unsigned) r->o_ss.channels-1); +        used_frames = (unsigned) av_resample(r->ffmpeg.state, +                                             q, p, +                                             &consumed_frames, +                                             (int) in, (int) *out_n_frames, +                                             c >= (unsigned) (r->o_ss.channels-1));          pa_memblock_release(b); @@ -1562,8 +1575,8 @@ static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned          pa_assert(consumed_frames <= (int) in);          if (consumed_frames < (int) in) {              r->ffmpeg.buf[c].memblock = b; -            r->ffmpeg.buf[c].index = consumed_frames * sizeof(int16_t); -            r->ffmpeg.buf[c].length = (in - consumed_frames) * sizeof(int16_t); +            r->ffmpeg.buf[c].index = (size_t) consumed_frames * sizeof(int16_t); +            r->ffmpeg.buf[c].length = (size_t) (in - (unsigned) consumed_frames) * sizeof(int16_t);          } else              pa_memblock_unref(b); @@ -1605,7 +1618,7 @@ static int ffmpeg_init(pa_resampler *r) {       * internally only uses these hardcoded values, so let's use them       * here for now as well until ffmpeg makes this configurable. */ -    if (!(r->ffmpeg.state = av_resample_init(r->o_ss.rate, r->i_ss.rate, 16, 10, 0, 0.8))) +    if (!(r->ffmpeg.state = av_resample_init((int) r->o_ss.rate, (int) r->i_ss.rate, 16, 10, 0, 0.8)))          return -1;      r->impl_free = ffmpeg_free; diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h index 5e302a9b..87110cc2 100644 --- a/src/pulsecore/resampler.h +++ b/src/pulsecore/resampler.h @@ -49,9 +49,10 @@ typedef enum pa_resample_method {  } pa_resample_method_t;  typedef enum pa_resample_flags { -    PA_RESAMPLER_VARIABLE_RATE = 1, -    PA_RESAMPLER_NO_REMAP = 2,  /* implies NO_REMIX */ -    PA_RESAMPLER_NO_REMIX = 4 +    PA_RESAMPLER_VARIABLE_RATE = 0x0001U, +    PA_RESAMPLER_NO_REMAP      = 0x0002U,  /* implies NO_REMIX */ +    PA_RESAMPLER_NO_REMIX      = 0x0004U, +    PA_RESAMPLER_NO_LFE        = 0x0008U  } pa_resample_flags_t;  pa_resampler* pa_resampler_new( diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h index 705de48f..aa2cdace 100644 --- a/src/pulsecore/rtclock.h +++ b/src/pulsecore/rtclock.h @@ -23,6 +23,7 @@  ***/  #include <pulsecore/macro.h> +#include <pulse/sample.h>  struct timeval; diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index a67a5516..543262bc 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -394,7 +394,7 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {  #endif  #endif -        r = poll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? (timeout.tv_sec*1000) + (timeout.tv_usec / 1000) : -1); +        r = poll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1);      if (r < 0) {          if (errno == EAGAIN || errno == EINTR) diff --git a/src/pulsecore/rtsig.c b/src/pulsecore/rtsig.c index 4df217c3..4cd6aa8f 100644 --- a/src/pulsecore/rtsig.c +++ b/src/pulsecore/rtsig.c @@ -40,7 +40,7 @@ static void _free_rtsig(void *p) {      pa_rtsig_put(PA_PTR_TO_INT(p));  } -PA_STATIC_FLIST_DECLARE(rtsig_flist, pa_make_power_of_two(SIGRTMAX-SIGRTMIN+1), NULL); +PA_STATIC_FLIST_DECLARE(rtsig_flist, pa_make_power_of_two((unsigned) (SIGRTMAX-SIGRTMIN+1)), NULL);  PA_STATIC_TLS_DECLARE(rtsig_tls, _free_rtsig);  static pa_atomic_t rtsig_current = PA_ATOMIC_INIT(-1); diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c index b42b79d1..9f0f795c 100644 --- a/src/pulsecore/sample-util.c +++ b/src/pulsecore/sample-util.c @@ -31,6 +31,8 @@  #include <liboil/liboilfuncs.h>  #include <liboil/liboil.h> +#include <pulse/timeval.h> +  #include <pulsecore/log.h>  #include <pulsecore/macro.h>  #include <pulsecore/g711.h> @@ -85,7 +87,6 @@ static uint8_t silence_byte(pa_sample_format_t format) {          default:              pa_assert_not_reached();      } -    return 0;  }  void* pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) { @@ -97,56 +98,62 @@ void* pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {      return p;  } -static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_sample_spec *spec) { -    unsigned k; - -    pa_assert(streams); -    pa_assert(spec); +static void calc_linear_integer_volume(int32_t linear[], const pa_cvolume *volume) { +    unsigned channel; -    for (k = 0; k < nstreams; k++) { -        unsigned channel; +    pa_assert(linear); +    pa_assert(volume); -        for (channel = 0; channel < spec->channels; channel++) { -            pa_mix_info *m = streams + k; -            m->linear[channel].i = (int32_t) (pa_sw_volume_to_linear(m->volume.values[channel]) * 0x10000); -        } -    } +    for (channel = 0; channel < volume->channels; channel++) +        linear[channel] = (int32_t) lrint(pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);  } -static void calc_linear_integer_volume(int32_t linear[], const pa_cvolume *volume) { +static void calc_linear_float_volume(float linear[], const pa_cvolume *volume) {      unsigned channel;      pa_assert(linear);      pa_assert(volume);      for (channel = 0; channel < volume->channels; channel++) -        linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000); +        linear[channel] = (float) pa_sw_volume_to_linear(volume->values[channel]);  } -static void calc_linear_float_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_sample_spec *spec) { -    unsigned k; +static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec) { +    unsigned k, channel; +    float linear[PA_CHANNELS_MAX];      pa_assert(streams);      pa_assert(spec); +    pa_assert(volume); + +    calc_linear_float_volume(linear, volume);      for (k = 0; k < nstreams; k++) { -        unsigned channel;          for (channel = 0; channel < spec->channels; channel++) {              pa_mix_info *m = streams + k; -            m->linear[channel].f = pa_sw_volume_to_linear(m->volume.values[channel]); +            m->linear[channel].i = (int32_t) lrint(pa_sw_volume_to_linear(m->volume.values[channel]) * linear[channel] * 0x10000);          }      }  } -static void calc_linear_float_volume(float linear[], const pa_cvolume *volume) { -    unsigned channel; +static void calc_linear_float_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec) { +    unsigned k, channel; +    float linear[PA_CHANNELS_MAX]; -    pa_assert(linear); +    pa_assert(streams); +    pa_assert(spec);      pa_assert(volume); -    for (channel = 0; channel < volume->channels; channel++) -        linear[channel] = pa_sw_volume_to_linear(volume->values[channel]); +    calc_linear_float_volume(linear, volume); + +    for (k = 0; k < nstreams; k++) { + +        for (channel = 0; channel < spec->channels; channel++) { +            pa_mix_info *m = streams + k; +            m->linear[channel].f = (float) (pa_sw_volume_to_linear(m->volume.values[channel]) * linear[channel]); +        } +    }  }  size_t pa_mix( @@ -160,7 +167,8 @@ size_t pa_mix(      pa_cvolume full_volume;      unsigned k; -    size_t d = 0; +    unsigned z; +    void *end;      pa_assert(streams);      pa_assert(data); @@ -170,45 +178,46 @@ size_t pa_mix(      if (!volume)          volume = pa_cvolume_reset(&full_volume, spec->channels); +    if (mute || pa_cvolume_is_muted(volume) || nstreams <= 0) { +        pa_silence_memory(data, length, spec); +        return length; +    } +      for (k = 0; k < nstreams; k++)          streams[k].ptr = (uint8_t*) pa_memblock_acquire(streams[k].chunk.memblock) + streams[k].chunk.index; +    for (z = 0; z < nstreams; z++) +        if (length > streams[z].chunk.length) +            length = streams[z].chunk.length; + +    end = (uint8_t*) data + length; +      switch (spec->format) {          case PA_SAMPLE_S16NE:{              unsigned channel = 0; -            int32_t linear[PA_CHANNELS_MAX]; -            calc_linear_integer_stream_volumes(streams, nstreams, spec); -            calc_linear_integer_volume(linear, volume); +            calc_linear_integer_stream_volumes(streams, nstreams, volume, spec); -            for (d = 0;; d += sizeof(int16_t)) { +            while (data < end) {                  int32_t sum = 0;                  unsigned i; -                if (PA_UNLIKELY(d >= length)) -                    goto finish; -                  for (i = 0; i < nstreams; i++) {                      pa_mix_info *m = streams + i;                      int32_t v, cv = m->linear[channel].i; -                    if (PA_UNLIKELY(d >= m->chunk.length)) -                        goto finish; - -                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0)) -                        v = 0; -                    else { -                        v = *((int16_t*) m->ptr); -                        v = (v * cv) / 0x10000; -                    } +                    if (PA_UNLIKELY(cv <= 0)) +                        continue; +                    v = *((int16_t*) m->ptr); +                    v = (v * cv) / 0x10000;                      sum += v; +                      m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);                  }                  sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF); -                sum = (sum * linear[channel]) / 0x10000;                  *((int16_t*) data) = (int16_t) sum;                  data = (uint8_t*) data + sizeof(int16_t); @@ -222,38 +231,28 @@ size_t pa_mix(          case PA_SAMPLE_S16RE:{              unsigned channel = 0; -            int32_t linear[PA_CHANNELS_MAX]; -            calc_linear_integer_stream_volumes(streams, nstreams, spec); -            calc_linear_integer_volume(linear, volume); +            calc_linear_integer_stream_volumes(streams, nstreams, volume, spec); -            for (d = 0;; d += sizeof(int16_t)) { +            while (data < end) {                  int32_t sum = 0;                  unsigned i; -                if (PA_UNLIKELY(d >= length)) -                    goto finish; -                  for (i = 0; i < nstreams; i++) {                      pa_mix_info *m = streams + i;                      int32_t v, cv = m->linear[channel].i; -                    if (PA_UNLIKELY(d >= m->chunk.length)) -                        goto finish; - -                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0)) -                        v = 0; -                    else { -                        v = PA_INT16_SWAP(*((int16_t*) m->ptr)); -                        v = (v * cv) / 0x10000; -                    } +                    if (PA_UNLIKELY(cv <= 0)) +                        continue; +                    v = PA_INT16_SWAP(*((int16_t*) m->ptr)); +                    v = (v * cv) / 0x10000;                      sum += v; +                      m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);                  }                  sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF); -                sum = (sum * linear[channel]) / 0x10000;                  *((int16_t*) data) = PA_INT16_SWAP((int16_t) sum);                  data = (uint8_t*) data + sizeof(int16_t); @@ -267,39 +266,29 @@ size_t pa_mix(          case PA_SAMPLE_S32NE:{              unsigned channel = 0; -            int32_t linear[PA_CHANNELS_MAX]; -            calc_linear_integer_stream_volumes(streams, nstreams, spec); -            calc_linear_integer_volume(linear, volume); +            calc_linear_integer_stream_volumes(streams, nstreams, volume, spec); -            for (d = 0;; d += sizeof(int32_t)) { +            while (data < end) {                  int64_t sum = 0;                  unsigned i; -                if (PA_UNLIKELY(d >= length)) -                    goto finish; -                  for (i = 0; i < nstreams; i++) {                      pa_mix_info *m = streams + i; -                    int64_t v;                      int32_t cv = m->linear[channel].i; +                    int64_t v; -                    if (PA_UNLIKELY(d >= m->chunk.length)) -                        goto finish; - -                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0)) -                        v = 0; -                    else { -                        v = *((int32_t*) m->ptr); -                        v = (v * cv) / 0x10000; -                    } +                    if (PA_UNLIKELY(cv <= 0)) +                        continue; +                    v = *((int32_t*) m->ptr); +                    v = (v * cv) / 0x10000;                      sum += v; +                      m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);                  }                  sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL); -                sum = (sum * linear[channel]) / 0x10000;                  *((int32_t*) data) = (int32_t) sum;                  data = (uint8_t*) data + sizeof(int32_t); @@ -313,39 +302,29 @@ size_t pa_mix(          case PA_SAMPLE_S32RE:{              unsigned channel = 0; -            int32_t linear[PA_CHANNELS_MAX]; -            calc_linear_integer_stream_volumes(streams, nstreams, spec); -            calc_linear_integer_volume(linear, volume); +            calc_linear_integer_stream_volumes(streams, nstreams, volume, spec); -            for (d = 0;; d += sizeof(int32_t)) { +            while (data < end) {                  int64_t sum = 0;                  unsigned i; -                if (PA_UNLIKELY(d >= length)) -                    goto finish; -                  for (i = 0; i < nstreams; i++) {                      pa_mix_info *m = streams + i; -                    int64_t v;                      int32_t cv = m->linear[channel].i; +                    int64_t v; -                    if (PA_UNLIKELY(d >= m->chunk.length)) -                        goto finish; - -                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0)) -                        v = 0; -                    else { -                        v = PA_INT32_SWAP(*((int32_t*) m->ptr)); -                        v = (v * cv) / 0x10000; -                    } +                    if (PA_UNLIKELY(cv <= 0)) +                        continue; +                    v = PA_INT32_SWAP(*((int32_t*) m->ptr)); +                    v = (v * cv) / 0x10000;                      sum += v; +                      m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);                  }                  sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL); -                sum = (sum * linear[channel]) / 0x10000;                  *((int32_t*) data) = PA_INT32_SWAP((int32_t) sum);                  data = (uint8_t*) data + sizeof(int32_t); @@ -359,37 +338,27 @@ size_t pa_mix(          case PA_SAMPLE_U8: {              unsigned channel = 0; -            int32_t linear[PA_CHANNELS_MAX]; -            calc_linear_integer_stream_volumes(streams, nstreams, spec); -            calc_linear_integer_volume(linear, volume); +            calc_linear_integer_stream_volumes(streams, nstreams, volume, spec); -            for (d = 0;; d ++) { +            while (data < end) {                  int32_t sum = 0;                  unsigned i; -                if (PA_UNLIKELY(d >= length)) -                    goto finish; -                  for (i = 0; i < nstreams; i++) {                      pa_mix_info *m = streams + i;                      int32_t v, cv = m->linear[channel].i; -                    if (PA_UNLIKELY(d >= m->chunk.length)) -                        goto finish; - -                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0)) -                        v = 0; -                    else { -                        v = (int32_t) *((uint8_t*) m->ptr) - 0x80; -                        v = (v * cv) / 0x10000; -                    } +                    if (PA_UNLIKELY(cv <= 0)) +                        continue; +                    v = (int32_t) *((uint8_t*) m->ptr) - 0x80; +                    v = (v * cv) / 0x10000;                      sum += v; +                      m->ptr = (uint8_t*) m->ptr + 1;                  } -                sum = (sum * linear[channel]) / 0x10000;                  sum = PA_CLAMP_UNLIKELY(sum, -0x80, 0x7F);                  *((uint8_t*) data) = (uint8_t) (sum + 0x80); @@ -404,39 +373,29 @@ size_t pa_mix(          case PA_SAMPLE_ULAW: {              unsigned channel = 0; -            int32_t linear[PA_CHANNELS_MAX]; -            calc_linear_integer_stream_volumes(streams, nstreams, spec); -            calc_linear_integer_volume(linear, volume); +            calc_linear_integer_stream_volumes(streams, nstreams, volume, spec); -            for (d = 0;; d ++) { +            while (data < end) {                  int32_t sum = 0;                  unsigned i; -                if (PA_UNLIKELY(d >= length)) -                    goto finish; -                  for (i = 0; i < nstreams; i++) {                      pa_mix_info *m = streams + i;                      int32_t v, cv = m->linear[channel].i; -                    if (PA_UNLIKELY(d >= m->chunk.length)) -                        goto finish; - -                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0)) -                        v = 0; -                    else { -                        v = (int32_t) st_ulaw2linear16(*((uint8_t*) m->ptr)); -                        v = (v * cv) / 0x10000; -                    } +                    if (PA_UNLIKELY(cv <= 0)) +                        continue; +                    v = (int32_t) st_ulaw2linear16(*((uint8_t*) m->ptr)); +                    v = (v * cv) / 0x10000;                      sum += v; +                      m->ptr = (uint8_t*) m->ptr + 1;                  }                  sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF); -                sum = (sum * linear[channel]) / 0x10000; -                *((uint8_t*) data) = (uint8_t) st_14linear2ulaw(sum >> 2); +                *((uint8_t*) data) = (uint8_t) st_14linear2ulaw((int16_t) sum >> 2);                  data = (uint8_t*) data + 1; @@ -449,39 +408,29 @@ size_t pa_mix(          case PA_SAMPLE_ALAW: {              unsigned channel = 0; -            int32_t linear[PA_CHANNELS_MAX]; -            calc_linear_integer_stream_volumes(streams, nstreams, spec); -            calc_linear_integer_volume(linear, volume); +            calc_linear_integer_stream_volumes(streams, nstreams, volume, spec); -            for (d = 0;; d ++) { +            while (data < end) {                  int32_t sum = 0;                  unsigned i; -                if (PA_UNLIKELY(d >= length)) -                    goto finish; -                  for (i = 0; i < nstreams; i++) {                      pa_mix_info *m = streams + i;                      int32_t v, cv = m->linear[channel].i; -                    if (PA_UNLIKELY(d >= m->chunk.length)) -                        goto finish; - -                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0)) -                        v = 0; -                    else { -                        v = (int32_t) st_alaw2linear16(*((uint8_t*) m->ptr)); -                        v = (v * cv) / 0x10000; -                    } +                    if (PA_UNLIKELY(cv <= 0)) +                        continue; +                    v = (int32_t) st_alaw2linear16(*((uint8_t*) m->ptr)); +                    v = (v * cv) / 0x10000;                      sum += v; +                      m->ptr = (uint8_t*) m->ptr + 1;                  }                  sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF); -                sum = (sum * linear[channel]) / 0x10000; -                *((uint8_t*) data) = (uint8_t) st_13linear2alaw(sum >> 3); +                *((uint8_t*) data) = (uint8_t) st_13linear2alaw((int16_t) sum >> 3);                  data = (uint8_t*) data + 1; @@ -494,37 +443,27 @@ size_t pa_mix(          case PA_SAMPLE_FLOAT32NE: {              unsigned channel = 0; -            float linear[PA_CHANNELS_MAX]; -            calc_linear_float_stream_volumes(streams, nstreams, spec); -            calc_linear_float_volume(linear, volume); +            calc_linear_float_stream_volumes(streams, nstreams, volume, spec); -            for (d = 0;; d += sizeof(float)) { +            while (data < end) {                  float sum = 0;                  unsigned i; -                if (PA_UNLIKELY(d >= length)) -                    goto finish; -                  for (i = 0; i < nstreams; i++) {                      pa_mix_info *m = streams + i;                      float v, cv = m->linear[channel].f; -                    if (PA_UNLIKELY(d >= m->chunk.length)) -                        goto finish; - -                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0)) -                        v = 0; -                    else { -                        v = *((float*) m->ptr); -                        v *= cv; -                    } +                    if (PA_UNLIKELY(cv <= 0)) +                        continue; +                    v = *((float*) m->ptr); +                    v *= cv;                      sum += v; +                      m->ptr = (uint8_t*) m->ptr + sizeof(float);                  } -                sum *= linear[channel];                  *((float*) data) = sum;                  data = (uint8_t*) data + sizeof(float); @@ -540,38 +479,27 @@ size_t pa_mix(              unsigned channel = 0;              float linear[PA_CHANNELS_MAX]; -            calc_linear_float_stream_volumes(streams, nstreams, spec); -            calc_linear_float_volume(linear, volume); +            calc_linear_float_stream_volumes(streams, nstreams, volume, spec); -            for (d = 0;; d += sizeof(float)) { +            while (data < end) {                  float sum = 0;                  unsigned i; -                if (PA_UNLIKELY(d >= length)) -                    goto finish; -                  for (i = 0; i < nstreams; i++) {                      pa_mix_info *m = streams + i;                      float v, cv = m->linear[channel].f; -                    if (PA_UNLIKELY(d >= m->chunk.length)) -                        goto finish; - -                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0)) -                        v = 0; -                    else { -                        uint32_t z = *(uint32_t*) m->ptr; -                        z = PA_UINT32_SWAP(z); -                        v = *((float*) &z); -                        v *= cv; -                    } +                    if (PA_UNLIKELY(cv <= 0)) +                        continue; +                    v = PA_FLOAT32_SWAP(*(float*) m->ptr); +                    v *= cv;                      sum += v; +                      m->ptr = (uint8_t*) m->ptr + sizeof(float);                  } -                sum *= linear[channel]; -                *((uint32_t*) data) = PA_UINT32_SWAP(*(uint32_t*) &sum); +                *((float*) data) = PA_FLOAT32_SWAP(sum);                  data = (uint8_t*) data + sizeof(float); @@ -583,16 +511,14 @@ size_t pa_mix(          }          default: -            pa_log_error("ERROR: Unable to mix audio data of format %s.", pa_sample_format_to_string(spec->format)); +            pa_log_error("Unable to mix audio data of format %s.", pa_sample_format_to_string(spec->format));              pa_assert_not_reached();      } -finish: -      for (k = 0; k < nstreams; k++)          pa_memblock_release(streams[k].chunk.memblock); -    return d; +    return length;  } @@ -624,14 +550,15 @@ void pa_volume_memchunk(      switch (spec->format) {          case PA_SAMPLE_S16NE: { -            int16_t *d; -            size_t n; +            int16_t *d, *e;              unsigned channel;              int32_t linear[PA_CHANNELS_MAX];              calc_linear_integer_volume(linear, volume); -            for (channel = 0, d = ptr, n = c->length/sizeof(int16_t); n > 0; d++, n--) { +            e = (int16_t*) ptr + c->length/sizeof(int16_t); + +            for (channel = 0, d = ptr; d < e; d++) {                  int32_t t;                  t = (int32_t)(*d); @@ -646,17 +573,18 @@ void pa_volume_memchunk(          }          case PA_SAMPLE_S16RE: { -            int16_t *d; -            size_t n; +            int16_t *d, *e;              unsigned channel;              int32_t linear[PA_CHANNELS_MAX];              calc_linear_integer_volume(linear, volume); -            for (channel = 0, d = ptr, n = c->length/sizeof(int16_t); n > 0; d++, n--) { +            e = (int16_t*) ptr + c->length/sizeof(int16_t); + +            for (channel = 0, d = ptr; d < e; d++) {                  int32_t t; -                t = (int32_t)(PA_INT16_SWAP(*d)); +                t = (int32_t) PA_INT16_SWAP(*d);                  t = (t * linear[channel]) / 0x10000;                  t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);                  *d = PA_INT16_SWAP((int16_t) t); @@ -669,14 +597,15 @@ void pa_volume_memchunk(          }          case PA_SAMPLE_S32NE: { -            int32_t *d; -            size_t n; +            int32_t *d, *e;              unsigned channel;              int32_t linear[PA_CHANNELS_MAX];              calc_linear_integer_volume(linear, volume); -            for (channel = 0, d = ptr, n = c->length/sizeof(int32_t); n > 0; d++, n--) { +            e = (int32_t*) ptr + c->length/sizeof(int32_t); + +            for (channel = 0, d = ptr; d < e; d++) {                  int64_t t;                  t = (int64_t)(*d); @@ -691,17 +620,18 @@ void pa_volume_memchunk(          }          case PA_SAMPLE_S32RE: { -            int32_t *d; -            size_t n; +            int32_t *d, *e;              unsigned channel;              int32_t linear[PA_CHANNELS_MAX];              calc_linear_integer_volume(linear, volume); -            for (channel = 0, d = ptr, n = c->length/sizeof(int32_t); n > 0; d++, n--) { +            e = (int32_t*) ptr + c->length/sizeof(int32_t); + +            for (channel = 0, d = ptr; d < e; d++) {                  int64_t t; -                t = (int64_t)(PA_INT32_SWAP(*d)); +                t = (int64_t) PA_INT32_SWAP(*d);                  t = (t * linear[channel]) / 0x10000;                  t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);                  *d = PA_INT32_SWAP((int32_t) t); @@ -714,14 +644,15 @@ void pa_volume_memchunk(          }          case PA_SAMPLE_U8: { -            uint8_t *d; -            size_t n; +            uint8_t *d, *e;              unsigned channel;              int32_t linear[PA_CHANNELS_MAX];              calc_linear_integer_volume(linear, volume); -            for (channel = 0, d = ptr, n = c->length; n > 0; d++, n--) { +            e = (uint8_t*) ptr + c->length; + +            for (channel = 0, d = ptr; d < e; d++) {                  int32_t t;                  t = (int32_t) *d - 0x80; @@ -736,20 +667,21 @@ void pa_volume_memchunk(          }          case PA_SAMPLE_ULAW: { -            uint8_t *d; -            size_t n; +            uint8_t *d, *e;              unsigned channel;              int32_t linear[PA_CHANNELS_MAX];              calc_linear_integer_volume(linear, volume); -            for (channel = 0, d = ptr, n = c->length; n > 0; d++, n--) { +            e = (uint8_t*) ptr + c->length; + +            for (channel = 0, d = ptr; d < e; d++) {                  int32_t t;                  t = (int32_t) st_ulaw2linear16(*d);                  t = (t * linear[channel]) / 0x10000;                  t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF); -                *d = (uint8_t) st_14linear2ulaw(t >> 2); +                *d = (uint8_t) st_14linear2ulaw((int16_t) t >> 2);                  if (PA_UNLIKELY(++channel >= spec->channels))                      channel = 0; @@ -758,20 +690,21 @@ void pa_volume_memchunk(          }          case PA_SAMPLE_ALAW: { -            uint8_t *d; -            size_t n; +            uint8_t *d, *e;              unsigned channel;              int32_t linear[PA_CHANNELS_MAX];              calc_linear_integer_volume(linear, volume); -            for (channel = 0, d = ptr, n = c->length; n > 0; d++, n--) { +            e = (uint8_t*) ptr + c->length; + +            for (channel = 0, d = ptr; d < e; d++) {                  int32_t t;                  t = (int32_t) st_alaw2linear16(*d);                  t = (t * linear[channel]) / 0x10000;                  t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF); -                *d = (uint8_t) st_13linear2alaw(t >> 3); +                *d = (uint8_t) st_13linear2alaw((int16_t) t >> 3);                  if (PA_UNLIKELY(++channel >= spec->channels))                      channel = 0; @@ -786,8 +719,8 @@ void pa_volume_memchunk(              unsigned channel;              d = ptr; -            skip = spec->channels * sizeof(float); -            n = c->length/sizeof(float)/spec->channels; +            skip = (int) (spec->channels * sizeof(float)); +            n = (unsigned) (c->length/sizeof(float)/spec->channels);              for (channel = 0; channel < spec->channels; channel ++) {                  float v, *t; @@ -797,28 +730,26 @@ void pa_volume_memchunk(                  v = (float) pa_sw_volume_to_linear(volume->values[channel]);                  t = d + channel; -                oil_scalarmult_f32(t, skip, t, skip, &v, n); +                oil_scalarmult_f32(t, skip, t, skip, &v, (int) n);              }              break;          }          case PA_SAMPLE_FLOAT32RE: { -            uint32_t *d; -            size_t n; +            float *d, *e;              unsigned channel;              float linear[PA_CHANNELS_MAX];              calc_linear_float_volume(linear, volume); -            for (channel = 0, d = ptr, n = c->length/sizeof(float); n > 0; d++, n--) { +            e = (float*) ptr + c->length/sizeof(float); + +            for (channel = 0, d = ptr; d < e; d++) {                  float t; -                uint32_t z; -                z = PA_UINT32_SWAP(*d); -                t = *(float*) &z; +                t = PA_FLOAT32_SWAP(*d);                  t *= linear[channel]; -                z = *(uint32_t*) &t; -                *d = PA_UINT32_SWAP(z); +                *d = PA_FLOAT32_SWAP(t);                  if (PA_UNLIKELY(++channel >= spec->channels))                      channel = 0; @@ -846,7 +777,7 @@ size_t pa_frame_align(size_t l, const pa_sample_spec *ss) {      return (l/fs) * fs;  } -int pa_frame_aligned(size_t l, const pa_sample_spec *ss) { +pa_bool_t pa_frame_aligned(size_t l, const pa_sample_spec *ss) {      size_t fs;      pa_assert(ss); @@ -877,7 +808,7 @@ void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, u          d = (uint8_t*) dst + c * ss;          for (j = 0; j < n; j ++) { -            oil_memcpy(d, s, ss); +            oil_memcpy(d, s, (int) ss);              s = (uint8_t*) s + ss;              d = (uint8_t*) d + fs;          } @@ -905,7 +836,7 @@ void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss,          d = dst[c];          for (j = 0; j < n; j ++) { -            oil_memcpy(d, s, ss); +            oil_memcpy(d, s, (int) ss);              s = (uint8_t*) s + fs;              d = (uint8_t*) d + ss;          } @@ -1008,7 +939,7 @@ void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const vo      if (format == PA_SAMPLE_FLOAT32NE) {          float minus_one = -1.0, plus_one = 1.0; -        oil_clip_f32(d, dstr, s, sstr, n, &minus_one, &plus_one); +        oil_clip_f32(d, (int) dstr, s, (int) sstr, (int) n, &minus_one, &plus_one);      } else {          pa_assert(format == PA_SAMPLE_FLOAT32RE); @@ -1017,7 +948,7 @@ void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const vo              float f;              f = PA_FLOAT32_SWAP(*s); -            f = PA_CLAMP_UNLIKELY(f, -1.0, 1.0); +            f = PA_CLAMP_UNLIKELY(f, -1.0f, 1.0f);              *d = PA_FLOAT32_SWAP(f);              s = (const float*) ((const uint8_t*) s + sstr); @@ -1025,3 +956,34 @@ void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const vo          }      }  } + +/* Similar to pa_bytes_to_usec() but rounds up, not down */ + +pa_usec_t pa_bytes_to_usec_round_up(uint64_t length, const pa_sample_spec *spec) { +    size_t fs; +    pa_usec_t usec; + +    pa_assert(spec); + +    fs = pa_frame_size(spec); +    length = (length + fs - 1) / fs; + +    usec = (pa_usec_t) length * PA_USEC_PER_SEC; + +    return (usec + spec->rate - 1) / spec->rate; +} + +/* Similar to pa_usec_to_bytes() but rounds up, not down */ + +size_t pa_usec_to_bytes_round_up(pa_usec_t t, const pa_sample_spec *spec) { +    uint64_t u; +    pa_assert(spec); + +    u = (uint64_t) t * (uint64_t) spec->rate; + +    u = (u + PA_USEC_PER_SEC - 1) / PA_USEC_PER_SEC; + +    u *= pa_frame_size(spec); + +    return (size_t) u; +} diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h index cef70750..2fe2c81d 100644 --- a/src/pulsecore/sample-util.h +++ b/src/pulsecore/sample-util.h @@ -71,11 +71,14 @@ void pa_volume_memchunk(  size_t pa_frame_align(size_t l, const pa_sample_spec *ss) PA_GCC_PURE; -int pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE; +pa_bool_t pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;  void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n);  void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n);  void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n); +pa_usec_t pa_bytes_to_usec_round_up(uint64_t length, const pa_sample_spec *spec); +size_t pa_usec_to_bytes_round_up(pa_usec_t t, const pa_sample_spec *spec); +  #endif diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c index 41670f27..159c4655 100644 --- a/src/pulsecore/sconv-s16le.c +++ b/src/pulsecore/sconv-s16le.c @@ -70,13 +70,13 @@ void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b) {      for (; n > 0; n--) {          int16_t s = *(a++); -        *(b++) = ((float) INT16_FROM(s))/0x7FFF; +        *(b++) = ((float) INT16_FROM(s))/(float) 0x7FFF;      }  #else  {      static const double add = 0, factor = 1.0/0x7FFF; -    oil_scaleconv_f32_s16(b, a, n, &add, &factor); +    oil_scaleconv_f32_s16(b, a, (int) n, &add, &factor);  }  #endif  } @@ -95,7 +95,7 @@ void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b) {  #else  {      static const double add = 0, factor = 1.0/0x7FFFFFFF; -    oil_scaleconv_f32_s32(b, a, n, &add, &factor); +    oil_scaleconv_f32_s32(b, a, (int) n, &add, &factor);  }  #endif  } @@ -110,15 +110,15 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {          int16_t s;          float v = *(a++); -        v = PA_CLAMP_UNLIKELY(v, -1, 1); -        s = (int16_t) (v * 0x7FFF); +        v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.f); +        s = (int16_t) lrintf(v * 0x7FFF);          *(b++) = INT16_TO(s);      }  #else  {      static const double add = 0, factor = 0x7FFF; -    oil_scaleconv_s16_f32(b, a, n, &add, &factor); +    oil_scaleconv_s16_f32(b, a, (int) n, &add, &factor);  }  #endif  } @@ -133,15 +133,15 @@ void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b) {          int32_t s;          float v = *(a++); -        v = PA_CLAMP_UNLIKELY(v, -1, 1); -        s = (int32_t) ((double) v * (double) 0x7FFFFFFF); +        v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f); +        s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);          *(b++) = INT32_TO(s);      }  #else  {      static const double add = 0, factor = 0x7FFFFFFF; -    oil_scaleconv_s32_f32(b, a, n, &add, &factor); +    oil_scaleconv_s32_f32(b, a, (int) n, &add, &factor);  }  #endif  } @@ -153,8 +153,7 @@ void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b) {      for (; n > 0; n--) {          int16_t s = *(a++);          float k = ((float) INT16_FROM(s))/0x7FFF; -        uint32_t *j = (uint32_t*) &k; -        *j = PA_UINT32_SWAP(*j); +        k = PA_FLOAT32_SWAP(k);          *(b++) = k;      }  } @@ -166,8 +165,7 @@ void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b) {      for (; n > 0; n--) {          int32_t s = *(a++);          float k = (float) (((double) INT32_FROM(s))/0x7FFFFFFF); -        uint32_t *j = (uint32_t*) &k; -        *j = PA_UINT32_SWAP(*j); +        k = PA_FLOAT32_SWAP(k);          *(b++) = k;      }  } @@ -179,10 +177,9 @@ void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b) {      for (; n > 0; n--) {          int16_t s;          float v = *(a++); -        uint32_t *j = (uint32_t*) &v; -        *j = PA_UINT32_SWAP(*j); -        v = PA_CLAMP_UNLIKELY(v, -1, 1); -        s = (int16_t) (v * 0x7FFF); +        v = PA_FLOAT32_SWAP(v); +        v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f); +        s = (int16_t) lrintf(v * 0x7FFF);          *(b++) = INT16_TO(s);      }  } @@ -194,10 +191,9 @@ void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b) {      for (; n > 0; n--) {          int32_t s;          float v = *(a++); -        uint32_t *j = (uint32_t*) &v; -        *j = PA_UINT32_SWAP(*j); -        v = PA_CLAMP_UNLIKELY(v, -1, 1); -        s = (int32_t) ((double) v * 0x7FFFFFFF); +        v = PA_FLOAT32_SWAP(v); +        v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f); +        s = (int32_t) lrint((double) v * 0x7FFFFFFF);          *(b++) = INT32_TO(s);      }  } @@ -219,7 +215,7 @@ void pa_sconv_s32le_to_s16re(unsigned n, const int32_t*a, int16_t *b) {      for (; n > 0; n--) {          int16_t s = (int16_t) (INT32_FROM(*a) >> 16); -        *b = PA_UINT32_SWAP(s); +        *b = PA_INT16_SWAP(s);          a++;          b++;      } @@ -241,7 +237,7 @@ void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b) {      pa_assert(b);      for (; n > 0; n--) { -        int32_t s = ((int32_t) PA_UINT16_SWAP(*a)) << 16; +        int32_t s = ((int32_t) PA_INT16_SWAP(*a)) << 16;          *b = INT32_TO(s);          a++;          b++; diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c index 581e4681..6c4d420e 100644 --- a/src/pulsecore/sconv.c +++ b/src/pulsecore/sconv.c @@ -46,7 +46,7 @@ static void u8_to_float32ne(unsigned n, const uint8_t *a, float *b) {      pa_assert(a);      pa_assert(b); -    oil_scaleconv_f32_u8(b, a, n, &add, &factor); +    oil_scaleconv_f32_u8(b, a, (int) n, &add, &factor);  }  static void u8_from_float32ne(unsigned n, const float *a, uint8_t *b) { @@ -55,7 +55,7 @@ static void u8_from_float32ne(unsigned n, const float *a, uint8_t *b) {      pa_assert(a);      pa_assert(b); -    oil_scaleconv_u8_f32(b, a, n, &add, &factor); +    oil_scaleconv_u8_f32(b, a, (int) n, &add, &factor);  }  static void u8_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) { @@ -64,9 +64,9 @@ static void u8_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {      pa_assert(a);      pa_assert(b); -    oil_conv_s16_u8(b, 2, a, 1, n); -    oil_scalaradd_s16(b, 2, b, 2, &add, n); -    oil_scalarmult_s16(b, 2, b, 2, &factor, n); +    oil_conv_s16_u8(b, 2, a, 1, (int) n); +    oil_scalaradd_s16(b, 2, b, 2, &add, (int) n); +    oil_scalarmult_s16(b, 2, b, 2, &factor, (int) n);  }  static void u8_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) { @@ -84,7 +84,7 @@ static void float32ne_to_float32ne(unsigned n, const float *a, float *b) {      pa_assert(a);      pa_assert(b); -    oil_memcpy(b, a, sizeof(float) * n); +    oil_memcpy(b, a, (int) (sizeof(float) * n));  }  static void float32re_to_float32ne(unsigned n, const float *a, float *b) { @@ -101,7 +101,7 @@ static void s16ne_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {      pa_assert(a);      pa_assert(b); -    oil_memcpy(b, a, sizeof(int16_t) * n); +    oil_memcpy(b, a, (int) (sizeof(int16_t) * n));  }  static void s16re_to_s16ne(unsigned n, const int16_t *a, int16_t *b) { @@ -109,7 +109,7 @@ static void s16re_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {      pa_assert(b);      for (; n > 0; n--, a++, b++) -        *b = PA_UINT16_SWAP(*a); +        *b = PA_INT16_SWAP(*a);  }  /* ulaw */ @@ -128,9 +128,9 @@ static void ulaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {      for (; n > 0; n--) {          float v = *(a++); -        v = PA_CLAMP_UNLIKELY(v, -1, 1); +        v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);          v *= 0x1FFF; -        *(b++) = st_14linear2ulaw((int16_t) v); +        *(b++) = st_14linear2ulaw((int16_t) lrintf(v));      }  } @@ -166,9 +166,9 @@ static void alaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {      for (; n > 0; n--, a++, b++) {          float v = *a; -        v = PA_CLAMP_UNLIKELY(v, -1, 1); +        v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);          v *= 0xFFF; -        *b = st_13linear2alaw((int16_t) v); +        *b = st_13linear2alaw((int16_t) lrintf(v));      }  } @@ -177,7 +177,7 @@ static void alaw_to_s16ne(unsigned n, const int8_t *a, int16_t *b) {      pa_assert(b);      for (; n > 0; n--, a++, b++) -        *b = st_alaw2linear16(*a); +        *b = st_alaw2linear16((uint8_t) *a);  }  static void alaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) { diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c index 298bf716..c59d247c 100644 --- a/src/pulsecore/shm.c +++ b/src/pulsecore/shm.c @@ -73,10 +73,10 @@  struct shm_marker PA_GCC_PACKED {      pa_atomic_t marker; /* 0xbeefcafe */      pa_atomic_t pid; -    uint64_t *_reserverd1; -    uint64_t *_reserverd2; -    uint64_t *_reserverd3; -    uint64_t *_reserverd4; +    uint64_t _reserved1; +    uint64_t _reserved2; +    uint64_t _reserved3; +    uint64_t _reserved4;  };  static char *segment_name(char *fn, size_t l, unsigned id) { @@ -105,7 +105,7 @@ int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {          m->size = size;  #ifdef MAP_ANONYMOUS -        if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED) { +        if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t) 0)) == MAP_FAILED) {              pa_log("mmap() failed: %s", pa_cstrerror(errno));              goto fail;          } @@ -138,12 +138,12 @@ int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {          m->size = size + PA_ALIGN(sizeof(struct shm_marker)); -        if (ftruncate(fd, m->size) < 0) { +        if (ftruncate(fd, (off_t) m->size) < 0) {              pa_log("ftruncate() failed: %s", pa_cstrerror(errno));              goto fail;          } -        if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { +        if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {              pa_log("mmap() failed: %s", pa_cstrerror(errno));              goto fail;          } @@ -235,7 +235,7 @@ void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {      /* Align this to multiples of the page size */      ptr = (uint8_t*) m->ptr + offset; -    o = (uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr); +    o = (size_t) ((uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr));      if (o > 0) {          ps = PA_PAGE_SIZE; @@ -289,9 +289,9 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {          goto fail;      } -    m->size = st.st_size; +    m->size = (size_t) st.st_size; -    if ((m->ptr = mmap(NULL, m->size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { +    if ((m->ptr = mmap(NULL, m->size, PROT_READ, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {          pa_log("mmap() failed: %s", pa_cstrerror(errno));          goto fail;      } diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 64a6cdf9..0e1224f1 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -75,7 +75,7 @@ void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cv      pa_assert(data);      if ((data->volume_is_set = !!volume)) -        data->volume = *volume; +        data->volume = data->virtual_volume = *volume;  }  void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) { @@ -108,6 +108,7 @@ static void reset_callbacks(pa_sink_input *i) {      i->kill = NULL;      i->get_latency = NULL;      i->state_change = NULL; +    i->may_move_to = NULL;  }  /* Called from main context */ @@ -119,6 +120,7 @@ pa_sink_input* pa_sink_input_new(      pa_sink_input *i;      pa_resampler *resampler = NULL;      char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; +    pa_channel_map original_cm;      pa_assert(core);      pa_assert(data); @@ -141,20 +143,25 @@ pa_sink_input* pa_sink_input_new(      pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec));      if (!data->channel_map_is_set) { -        if (data->sink->channel_map.channels == data->sample_spec.channels) +        if (pa_channel_map_compatible(&data->sink->channel_map, &data->sample_spec))              data->channel_map = data->sink->channel_map;          else -            pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT)); +            pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);      }      pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map)); -    pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels); +    pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec)); -    if (!data->volume_is_set) +    if (!data->volume_is_set) {          pa_cvolume_reset(&data->volume, data->sample_spec.channels); +        pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels); +    }      pa_return_null_if_fail(pa_cvolume_valid(&data->volume)); -    pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels); +    pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec)); + +    pa_return_null_if_fail(pa_cvolume_valid(&data->virtual_volume)); +    pa_return_null_if_fail(pa_cvolume_compatible(&data->virtual_volume, &data->sample_spec));      if (!data->muted_is_set)          data->muted = FALSE; @@ -165,6 +172,8 @@ pa_sink_input* pa_sink_input_new(      if (flags & PA_SINK_INPUT_FIX_RATE)          data->sample_spec.rate = data->sink->sample_spec.rate; +    original_cm = data->channel_map; +      if (flags & PA_SINK_INPUT_FIX_CHANNELS) {          data->sample_spec.channels = data->sink->sample_spec.channels;          data->channel_map = data->sink->channel_map; @@ -174,8 +183,7 @@ pa_sink_input* pa_sink_input_new(      pa_assert(pa_channel_map_valid(&data->channel_map));      /* Due to the fixing of the sample spec the volume might not match anymore */ -    if (data->volume.channels != data->sample_spec.channels) -        pa_cvolume_set(&data->volume, data->sample_spec.channels, pa_cvolume_avg(&data->volume)); +    pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map);      if (data->resample_method == PA_RESAMPLER_INVALID)          data->resample_method = core->resample_method; @@ -201,7 +209,8 @@ pa_sink_input* pa_sink_input_new(                        data->resample_method,                        ((flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |                        ((flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | -                      (core->disable_remixing || (flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) { +                      (core->disable_remixing || (flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | +                      (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {              pa_log_warn("Unsupported resampling operation.");              return NULL;          } @@ -226,7 +235,9 @@ pa_sink_input* pa_sink_input_new(      i->sample_spec = data->sample_spec;      i->channel_map = data->channel_map; +    i->virtual_volume = data->virtual_volume;      i->volume = data->volume; +      i->muted = data->muted;      if (data->sync_base) { @@ -535,7 +546,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa               * data, so let's just hand out silence */              pa_atomic_store(&i->thread_info.drained, 1); -            pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE); +            pa_memblockq_seek(i->thread_info.render_memblockq, (int64_t) slength, PA_SEEK_RELATIVE);              i->thread_info.playing_for = 0;              if (i->thread_info.underrun_for != (uint64_t) -1)                  i->thread_info.underrun_for += ilength; @@ -756,14 +767,11 @@ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec)      if (PA_SINK_INPUT_IS_LINKED(i->state))          pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); -    else { +    else          /* If this sink input is not realized yet, we have to touch           * the thread info data directly */ -        usec = fixup_latency(i->sink, usec);          i->thread_info.requested_sink_latency = usec; -        i->sink->thread_info.requested_latency_valid = FALSE; -    }      return usec;  } @@ -786,17 +794,34 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {  /* Called from main context */  void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { +    pa_sink_input_set_volume_data data; +      pa_sink_input_assert_ref(i);      pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));      pa_assert(volume); +    pa_assert(pa_cvolume_valid(volume)); +    pa_assert(pa_cvolume_compatible(volume, &i->sample_spec)); + +    data.sink_input = i; +    data.virtual_volume = *volume; +    data.volume = *volume; + +    /* If you change something here, consider looking into +     * module-flat-volume.c as well since it uses very similar +     * code. */ -    if (pa_cvolume_equal(&i->volume, volume)) +    if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], &data) < 0)          return; -    i->volume = *volume; +    if (!pa_cvolume_equal(&i->volume, &data.volume)) { +        i->volume = data.volume; +        pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &data.volume, 0, NULL) == 0); +    } -    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &i->volume, 0, NULL) == 0); -    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +    if (!pa_cvolume_equal(&i->virtual_volume, &data.virtual_volume)) { +        i->virtual_volume = data.virtual_volume; +        pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +    }  }  /* Called from main context */ @@ -804,7 +829,7 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {      pa_sink_input_assert_ref(i);      pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); -    return &i->volume; +    return &i->virtual_volume;  }  /* Called from main context */ @@ -887,6 +912,35 @@ pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {  }  /* Called from main context */ +pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { +    pa_sink_input_assert_ref(i); +    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); +    pa_sink_assert_ref(dest); + +    if (dest == i->sink) +        return TRUE; + +    if (i->flags & PA_SINK_INPUT_DONT_MOVE) +        return FALSE; + +    if (i->sync_next || i->sync_prev) { +        pa_log_warn("Moving synchronised streams not supported."); +        return FALSE; +    } + +    if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) { +        pa_log_warn("Failed to move sink input: too many inputs per sink."); +        return FALSE; +    } + +    if (i->may_move_to) +        if (!i->may_move_to(i, dest)) +            return FALSE; + +    return TRUE; +} + +/* Called from main context */  int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {      pa_resampler *new_resampler;      pa_sink *origin; @@ -902,18 +956,8 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {      if (dest == origin)          return 0; -    if (i->flags & PA_SINK_INPUT_DONT_MOVE) -        return -1; - -    if (i->sync_next || i->sync_prev) { -        pa_log_warn("Moving synchronised streams not supported."); +    if (!pa_sink_input_may_move_to(i, dest))          return -1; -    } - -    if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) { -        pa_log_warn("Failed to move sink input: too many inputs per sink."); -        return -1; -    }      /* Kill directly connected outputs */      while ((o = pa_idxset_first(i->direct_outputs, NULL))) { @@ -1032,6 +1076,9 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state      } else if (uncorking) { +        i->thread_info.underrun_for = (uint64_t) -1; +        i->thread_info.playing_for = 0; +          pa_log_debug("Requesting rewind due to uncorking");          /* OK, we're being uncorked. Make sure we're not rewound when diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index c07a7404..27125988 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -56,7 +56,8 @@ typedef enum pa_sink_input_flags {      PA_SINK_INPUT_NO_REMIX = 16,      PA_SINK_INPUT_FIX_FORMAT = 32,      PA_SINK_INPUT_FIX_RATE = 64, -    PA_SINK_INPUT_FIX_CHANNELS = 128 +    PA_SINK_INPUT_FIX_CHANNELS = 128, +    PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,  } pa_sink_input_flags_t;  struct pa_sink_input { @@ -89,6 +90,8 @@ struct pa_sink_input {      pa_sink_input *sync_prev, *sync_next; +    pa_cvolume virtual_volume; +      pa_cvolume volume;      pa_bool_t muted; @@ -154,10 +157,15 @@ struct pa_sink_input {      returns */      pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */ -    /* If non_NULL this function is called from thread context if the +    /* If non-NULL this function is called from thread context if the       * state changes. The old state is found in thread_info.state.  */      void (*state_change) (pa_sink_input *i, pa_sink_input_state_t state); /* may be NULL */ +    /* If non-NULL this function is called before this sink input is +     * move to a sink and if it returns FALSE the move will not +     * be allowed */ +    pa_bool_t (*may_move_to) (pa_sink_input *i, pa_sink *s); /* may be NULL */ +      struct {          pa_sink_input_state_t state;          pa_atomic_t drained; @@ -212,19 +220,22 @@ typedef struct pa_sink_input_new_data {      pa_sink *sink; +    pa_resample_method_t resample_method; + +    pa_sink_input *sync_base; +      pa_sample_spec sample_spec; -    pa_bool_t sample_spec_is_set;      pa_channel_map channel_map; -    pa_bool_t channel_map_is_set; -    pa_cvolume volume; -    pa_bool_t volume_is_set; -    pa_bool_t muted; -    pa_bool_t muted_is_set; +    pa_cvolume virtual_volume; -    pa_resample_method_t resample_method; +    pa_cvolume volume; +    pa_bool_t muted:1; -    pa_sink_input *sync_base; +    pa_bool_t sample_spec_is_set:1; +    pa_bool_t channel_map_is_set:1; +    pa_bool_t volume_is_set:1; +    pa_bool_t muted_is_set:1;  } pa_sink_input_new_data;  pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data); @@ -239,6 +250,12 @@ typedef struct pa_sink_input_move_hook_data {      pa_sink *destination;  } pa_sink_input_move_hook_data; +typedef struct pa_sink_set_input_volume_data { +  pa_sink_input *sink_input; +  pa_cvolume virtual_volume; +  pa_cvolume volume; +} pa_sink_input_set_volume_data; +  /* To be called by the implementing module only */  pa_sink_input* pa_sink_input_new( @@ -281,6 +298,7 @@ pa_bool_t pa_sink_input_get_mute(pa_sink_input *i);  pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);  int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest); +pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest);  pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 4102f31d..a6027e70 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -663,7 +663,6 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {          pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);          if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) { -            pa_log("adjusting volume ");              pa_memchunk_make_writable(result, 0);              if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))                  pa_silence_memchunk(result, &s->sample_spec); @@ -844,30 +843,55 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) {  /* Called from main thread */  void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {      pa_bool_t changed; +    pa_sink_set_volume_data data;      pa_sink_assert_ref(s);      pa_assert(PA_SINK_IS_LINKED(s->state));      pa_assert(volume); +    pa_assert(pa_cvolume_valid(volume)); +    pa_assert(pa_cvolume_compatible(volume, &s->sample_spec)); -    changed = !pa_cvolume_equal(volume, &s->volume); -    s->volume = *volume; +    data.sink = s; +    data.volume = *volume; + +    changed = !pa_cvolume_equal(&data.volume, &s->volume); + +    if (changed) { +        if (pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_SET_VOLUME], &data) < 0) +            return; + +        changed = !pa_cvolume_equal(&data.volume, &s->volume); +    } + +    s->volume = data.volume;      if (s->set_volume && s->set_volume(s) < 0)          s->set_volume = NULL;      if (!s->set_volume) -        pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, volume, 0, NULL); +        pa_sink_set_soft_volume(s, volume);      if (changed)          pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);  }  /* Called from main thread */ -const pa_cvolume *pa_sink_get_volume(pa_sink *s) { +void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) { +    pa_sink_assert_ref(s); +    pa_assert(volume); + +    if (PA_SINK_IS_LINKED(s->state)) +        pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, volume, 0, NULL); +    else +        s->thread_info.soft_volume = *volume; +} + +/* Called from main thread */ +const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {      pa_sink_assert_ref(s);      pa_assert(PA_SINK_IS_LINKED(s->state)); -    if (s->refresh_volume) { +    if (s->refresh_volume || force_refresh) {          struct pa_cvolume old_volume = s->volume;          if (s->get_volume && s->get_volume(s) < 0) @@ -904,12 +928,12 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {  }  /* Called from main thread */ -pa_bool_t pa_sink_get_mute(pa_sink *s) { +pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {      pa_sink_assert_ref(s);      pa_assert(PA_SINK_IS_LINKED(s->state)); -    if (s->refresh_muted) { +    if (s->refresh_muted || force_refresh) {          pa_bool_t old_muted = s->muted;          if (s->get_mute && s->get_mute(s) < 0) @@ -967,7 +991,7 @@ unsigned pa_sink_linked_by(pa_sink *s) {      ret = pa_idxset_size(s->inputs);      /* We add in the number of streams connected to us here. Please -     * not the asymmmetry to pa_sink_used_by()! */ +     * note the asymmmetry to pa_sink_used_by()! */      if (s->monitor_source)          ret += pa_source_linked_by(s->monitor_source); @@ -991,6 +1015,38 @@ unsigned pa_sink_used_by(pa_sink *s) {      return ret - s->n_corked;  } +/* Called from main thread */ +unsigned pa_sink_check_suspend(pa_sink *s) { +    unsigned ret; +    pa_sink_input *i; +    uint32_t idx; + +    pa_sink_assert_ref(s); +    pa_assert(PA_SINK_IS_LINKED(s->state)); + +    ret = 0; + +    for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { +        pa_sink_input_state_t st; + +        st = pa_sink_input_get_state(i); +        pa_assert(PA_SINK_INPUT_IS_LINKED(st)); + +        if (st == PA_SINK_INPUT_CORKED) +            continue; + +        if (i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND) +            continue; + +        ret ++; +    } + +    if (s->monitor_source) +        ret += pa_source_check_suspend(s->monitor_source); + +    return ret; +} +  /* Called from IO thread, except when it is not */  int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {      pa_sink *s = PA_SINK(o); @@ -1031,11 +1087,15 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse              pa_sink_input_set_state_within_thread(i, i->state); +            /* The requested latency of the sink input needs to be +             * fixed up and then configured on the sink */ + +            if (i->thread_info.requested_sink_latency != (pa_usec_t) -1) +                pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency); +              pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);              pa_sink_input_update_max_request(i, s->thread_info.max_request); -            pa_sink_invalidate_requested_latency(s); -              /* We don't rewind here automatically. This is left to the               * sink input implementor because some sink inputs need a               * slow start, i.e. need some time to buffer client @@ -1147,11 +1207,12 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse              if (i->attach)                  i->attach(i); +            if (i->thread_info.requested_sink_latency != (pa_usec_t) -1) +                pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency); +              pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);              pa_sink_input_update_max_request(i, s->thread_info.max_request); -            pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency); -              if (i->thread_info.state != PA_SINK_INPUT_CORKED) {                  pa_usec_t usec = 0;                  size_t nbytes; @@ -1413,7 +1474,6 @@ void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {  /* Called from IO thread */  void pa_sink_set_max_request(pa_sink *s, size_t max_request) { -    pa_sink_input *i;      void *state = NULL;      pa_sink_assert_ref(s); @@ -1424,6 +1484,8 @@ void pa_sink_set_max_request(pa_sink *s, size_t max_request) {      s->thread_info.max_request = max_request;      if (PA_SINK_IS_LINKED(s->thread_info.state)) { +        pa_sink_input *i; +          while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))              pa_sink_input_update_max_request(i, s->thread_info.max_request);      } diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 604be269..c5a73214 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -180,21 +180,22 @@ typedef enum pa_sink_message {  typedef struct pa_sink_new_data {      char *name; -    pa_bool_t namereg_fail;      pa_proplist *proplist;      const char *driver;      pa_module *module;      pa_sample_spec sample_spec; -    pa_bool_t sample_spec_is_set;      pa_channel_map channel_map; -    pa_bool_t channel_map_is_set; -      pa_cvolume volume; -    pa_bool_t volume_is_set; -    pa_bool_t muted; -    pa_bool_t muted_is_set; +    pa_bool_t muted :1; + +    pa_bool_t sample_spec_is_set:1; +    pa_bool_t channel_map_is_set:1; +    pa_bool_t volume_is_set:1; +    pa_bool_t muted_is_set:1; + +    pa_bool_t namereg_fail:1;  } pa_sink_new_data;  pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data); @@ -205,6 +206,11 @@ void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volum  void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute);  void pa_sink_new_data_done(pa_sink_new_data *data); +typedef struct pa_sink_set_volume_data { +  pa_sink *sink; +  pa_cvolume volume; +} pa_sink_set_volume_data; +  /* To be called exclusively by the sink driver, from main context */  pa_sink* pa_sink_new( @@ -239,12 +245,14 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend);  int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend);  void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume); -const pa_cvolume *pa_sink_get_volume(pa_sink *sink); +void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume); +const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);  void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute); -pa_bool_t pa_sink_get_mute(pa_sink *sink); +pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refres);  unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */  unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */ +unsigned pa_sink_check_suspend(pa_sink *s); /* Returns how many streams are active that don't allow suspensions */  #define pa_sink_get_state(s) ((s)->state)  /* To be called exclusively by the sink driver, from IO context */ diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c index e69a63da..6739effd 100644 --- a/src/pulsecore/socket-client.c +++ b/src/pulsecore/socket-client.c @@ -187,7 +187,7 @@ static void connect_defer_cb(pa_mainloop_api *m, pa_defer_event *e, void *userda      do_call(c);  } -static void connect_io_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { +static void connect_io_cb(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {      pa_socket_client *c = userdata;      pa_assert(m); @@ -283,7 +283,7 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size      else          pa_make_socket_low_delay(c->fd); -    if (do_connect(c, sa, salen) < 0) +    if (do_connect(c, sa, (socklen_t) salen) < 0)          return -1;      return 0; @@ -370,7 +370,7 @@ pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[  #ifdef HAVE_LIBASYNCNS -static void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { +static void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {      pa_socket_client *c = userdata;      struct addrinfo *res = NULL;      int ret; @@ -437,7 +437,7 @@ static void start_timeout(pa_socket_client *c) {      pa_assert(!c->timeout_event);      pa_gettimeofday(&tv); -    pa_timeval_add(&tv, CONNECT_TIMEOUT * 1000000); +    pa_timeval_add(&tv, CONNECT_TIMEOUT * PA_USEC_PER_SEC);      c->timeout_event = c->mainloop->time_new(c->mainloop, &tv, timeout_cb, c);  } diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c index 9885a02b..a600e0a6 100644 --- a/src/pulsecore/socket-server.c +++ b/src/pulsecore/socket-server.c @@ -199,7 +199,7 @@ pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *file      pa_make_socket_low_delay(fd); -    if (bind(fd, (struct sockaddr*) &sa, SUN_LEN(&sa)) < 0) { +    if (bind(fd, (struct sockaddr*) &sa, (socklen_t) SUN_LEN(&sa)) < 0) {          pa_log("bind(): %s", pa_cstrerror(errno));          goto fail;      } diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 8b2a29b9..c30c16eb 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -170,10 +170,10 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk          if (u->readf_function) {              fs = pa_frame_size(&i->sample_spec); -            n = u->readf_function(u->sndfile, p, length/fs); +            n = u->readf_function(u->sndfile, p, (sf_count_t) (length/fs));          } else {              fs = 1; -            n = sf_read_raw(u->sndfile, p, length); +            n = sf_read_raw(u->sndfile, p, (sf_count_t) length);          }          pa_memblock_release(tchunk.memblock); @@ -186,7 +186,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk              break;          } -        tchunk.length = n * fs; +        tchunk.length = (size_t) n * fs;          pa_memblockq_push(u->memblockq, &tchunk);          pa_memblock_unref(tchunk.memblock); @@ -310,8 +310,8 @@ int pa_play_file(              break;      } -    ss.rate = sfinfo.samplerate; -    ss.channels = sfinfo.channels; +    ss.rate = (uint32_t) sfinfo.samplerate; +    ss.channels = (uint8_t) sfinfo.channels;      if (!pa_sample_spec_valid(&ss)) {          pa_log("Unsupported sample format in file %s", fname); diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c index 3183ede6..380cef16 100644 --- a/src/pulsecore/sound-file.c +++ b/src/pulsecore/sound-file.c @@ -89,7 +89,7 @@ int pa_sound_file_load(          case SF_FORMAT_PCM_U8:          case SF_FORMAT_PCM_S8:              ss->format = PA_SAMPLE_S16NE; -            readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short; +            readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *_ptr, sf_count_t frames)) sf_readf_short;              break;          case SF_FORMAT_ULAW: @@ -104,12 +104,12 @@ int pa_sound_file_load(          case SF_FORMAT_DOUBLE:          default:              ss->format = PA_SAMPLE_FLOAT32NE; -            readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float; +            readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *_ptr, sf_count_t frames)) sf_readf_float;              break;      } -    ss->rate = sfinfo.samplerate; -    ss->channels = sfinfo.channels; +    ss->rate = (uint32_t) sfinfo.samplerate; +    ss->channels = (uint8_t) sfinfo.channels;      if (!pa_sample_spec_valid(ss)) {          pa_log("Unsupported sample format in file %s", fname); @@ -119,7 +119,7 @@ int pa_sound_file_load(      if (map)          pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_DEFAULT); -    if ((l = pa_frame_size(ss) * sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) { +    if ((l = pa_frame_size(ss) * (size_t) sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {          pa_log("File too large");          goto finish;      } @@ -131,7 +131,7 @@ int pa_sound_file_load(      ptr = pa_memblock_acquire(chunk->memblock);      if ((readf_function && readf_function(sf, ptr, sfinfo.frames) != sfinfo.frames) || -        (!readf_function && sf_read_raw(sf, ptr, l) != (sf_count_t) l)) { +        (!readf_function && sf_read_raw(sf, ptr, (sf_count_t) l) != (sf_count_t) l)) {          pa_log("Premature file end");          goto finish;      } @@ -189,15 +189,15 @@ int pa_sound_file_too_big_to_cache(const char *fname) {              break;      } -    ss.rate = sfinfo.samplerate; -    ss.channels = sfinfo.channels; +    ss.rate = (uint32_t) sfinfo.samplerate; +    ss.channels = (uint8_t) sfinfo.channels;      if (!pa_sample_spec_valid(&ss)) {          pa_log("Unsupported sample format in file %s", fname);          return -1;      } -    if ((pa_frame_size(&ss) * sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) { +    if ((pa_frame_size(&ss) * (size_t) sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {          pa_log("File too large: %s", fname);          return 1;      } diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index dbf3af94..376402fa 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -90,6 +90,7 @@ static void reset_callbacks(pa_source_output *o) {      o->kill = NULL;      o->get_latency = NULL;      o->state_change = NULL; +    o->may_move_to = NULL;  }  /* Called from main context */ @@ -124,14 +125,14 @@ pa_source_output* pa_source_output_new(      pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec));      if (!data->channel_map_is_set) { -        if (data->source->channel_map.channels == data->sample_spec.channels) +        if (pa_channel_map_compatible(&data->source->channel_map, &data->sample_spec))              data->channel_map = data->source->channel_map;          else -            pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT)); +            pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);      }      pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map)); -    pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels); +    pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec));      if (flags & PA_SOURCE_OUTPUT_FIX_FORMAT)          data->sample_spec.format = data->source->sample_spec.format; @@ -171,7 +172,8 @@ pa_source_output* pa_source_output_new(                        data->resample_method,                        ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |                        ((flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | -                      (core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) { +                      (core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | +                      (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {              pa_log_warn("Unsupported resampling operation.");              return NULL;          } @@ -397,7 +399,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {      if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) {          pa_log_debug("Delay queue overflow!"); -        pa_memblockq_seek(o->thread_info.delay_memblockq, chunk->length, PA_SEEK_RELATIVE); +        pa_memblockq_seek(o->thread_info.delay_memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE);      }      limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind; @@ -511,14 +513,11 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t      if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))          pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); -    else { +    else          /* If this source output is not realized yet, we have to touch           * the thread info data directly */ -        usec = fixup_latency(o->source, usec);          o->thread_info.requested_source_latency = usec; -        o->source->thread_info.requested_latency_valid = FALSE; -    }      return usec;  } @@ -595,6 +594,32 @@ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {      return o->resample_method;  } +pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) { +    pa_source_output_assert_ref(o); +    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); +    pa_source_assert_ref(dest); + +    if (dest == o->source) +        return TRUE; + +    if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE) +        return FALSE; + +    if (o->direct_on_input) +        return FALSE; + +    if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) { +        pa_log_warn("Failed to move source output: too many outputs per source."); +        return FALSE; +    } + +    if (o->may_move_to) +        if (!o->may_move_to(o, dest)) +            return FALSE; + +    return TRUE; +} +  /* Called from main context */  int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {      pa_source *origin; @@ -610,17 +635,9 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {      if (dest == origin)          return 0; -    if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE) +    if (!pa_source_output_may_move_to(o, dest))          return -1; -    if (o->direct_on_input) -        return -1; - -    if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) { -        pa_log_warn("Failed to move source output: too many outputs per source."); -        return -1; -    } -      if (o->thread_info.resampler &&          pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&          pa_channel_map_equal(&origin->channel_map, &dest->channel_map)) diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 61825b22..f011f9bd 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -52,7 +52,8 @@ typedef enum pa_source_output_flags {      PA_SOURCE_OUTPUT_NO_REMIX = 16,      PA_SOURCE_OUTPUT_FIX_FORMAT = 32,      PA_SOURCE_OUTPUT_FIX_RATE = 64, -    PA_SOURCE_OUTPUT_FIX_CHANNELS = 128 +    PA_SOURCE_OUTPUT_FIX_CHANNELS = 128, +    PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND = 256  } pa_source_output_flags_t;  struct pa_source_output { @@ -126,10 +127,15 @@ struct pa_source_output {      returns */      pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */ -    /* If non_NULL this function is called from thread context if the +    /* If non-NULL this function is called from thread context if the       * state changes. The old state is found in thread_info.state.  */      void (*state_change) (pa_source_output *o, pa_source_output_state_t state); /* may be NULL */ +    /* If non-NULL this function is called before this source output +     * is moved to a source and if it returns FALSE the move +     * will not be allowed */ +    pa_bool_t (*may_move_to) (pa_source_output *o, pa_source *s); /* may be NULL */ +      struct {          pa_source_output_state_t state; @@ -174,12 +180,13 @@ typedef struct pa_source_output_new_data {      pa_source *source; +    pa_resample_method_t resample_method; +      pa_sample_spec sample_spec; -    pa_bool_t sample_spec_is_set;      pa_channel_map channel_map; -    pa_bool_t channel_map_is_set; -    pa_resample_method_t resample_method; +    pa_bool_t sample_spec_is_set:1; +    pa_bool_t channel_map_is_set:1;  } pa_source_output_new_data;  pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data); @@ -219,6 +226,7 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *i, pa_usec_t *source_la  pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o); +pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest);  int pa_source_output_move_to(pa_source_output *o, pa_source *dest);  #define pa_source_output_get_state(o) ((o)->state) diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 31620690..7d927faa 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -520,18 +520,29 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {          s->set_volume = NULL;      if (!s->set_volume) -        pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, volume, 0, NULL); +        pa_source_set_soft_volume(s, volume);      if (changed)          pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);  }  /* Called from main thread */ -const pa_cvolume *pa_source_get_volume(pa_source *s) { +void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume) { +    pa_source_assert_ref(s); +    pa_assert(volume); + +    if (PA_SOURCE_IS_LINKED(s->state)) +        pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, volume, 0, NULL); +    else +        s->thread_info.soft_volume = *volume; +} + +/* Called from main thread */ +const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) {      pa_source_assert_ref(s);      pa_assert(PA_SOURCE_IS_LINKED(s->state)); -    if (s->refresh_volume) { +    if (s->refresh_volume || force_refresh) {          pa_cvolume old_volume = s->volume;          if (s->get_volume && s->get_volume(s) < 0) @@ -568,12 +579,12 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) {  }  /* Called from main thread */ -pa_bool_t pa_source_get_mute(pa_source *s) { +pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {      pa_source_assert_ref(s);      pa_assert(PA_SOURCE_IS_LINKED(s->state)); -    if (s->refresh_muted) { +    if (s->refresh_muted || force_refresh) {          pa_bool_t old_muted = s->muted;          if (s->get_mute && s->get_mute(s) < 0) @@ -634,6 +645,35 @@ unsigned pa_source_used_by(pa_source *s) {      return ret - s->n_corked;  } +/* Called from main thread */ +unsigned pa_source_check_suspend(pa_source *s) { +    unsigned ret; +    pa_source_output *o; +    uint32_t idx; + +    pa_source_assert_ref(s); +    pa_assert(PA_SOURCE_IS_LINKED(s->state)); + +    ret = 0; + +    for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx))) { +        pa_source_output_state_t st; + +        st = pa_source_output_get_state(o); +        pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(st)); + +        if (st == PA_SOURCE_OUTPUT_CORKED) +            continue; + +        if (o->flags & PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND) +            continue; + +        ret ++; +    } + +    return ret; +} +  /* Called from IO thread, except when it is not */  int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {      pa_source *s = PA_SOURCE(object); @@ -659,6 +699,9 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_              pa_source_output_set_state_within_thread(o, o->state); +            if (o->thread_info.requested_source_latency != (pa_usec_t) -1) +                pa_source_output_set_requested_latency_within_thread(o, o->thread_info.requested_source_latency); +              pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);              /* We don't just invalidate the requested latency here, diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index f4a17e8d..aaf904b4 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -168,21 +168,22 @@ typedef enum pa_source_message {  typedef struct pa_source_new_data {      char *name; -    pa_bool_t namereg_fail;      pa_proplist *proplist;      const char *driver;      pa_module *module;      pa_sample_spec sample_spec; -    pa_bool_t sample_spec_is_set;      pa_channel_map channel_map; -    pa_bool_t channel_map_is_set; -      pa_cvolume volume; -    pa_bool_t volume_is_set; -    pa_bool_t muted; -    pa_bool_t muted_is_set; +    pa_bool_t muted:1; + +    pa_bool_t volume_is_set:1; +    pa_bool_t muted_is_set:1; +    pa_bool_t sample_spec_is_set:1; +    pa_bool_t channel_map_is_set:1; + +    pa_bool_t namereg_fail:1;  } pa_source_new_data;  pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data); @@ -226,12 +227,14 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend);  int pa_source_suspend_all(pa_core *c, pa_bool_t suspend);  void pa_source_set_volume(pa_source *source, const pa_cvolume *volume); -const pa_cvolume *pa_source_get_volume(pa_source *source); +void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume); +const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh);  void pa_source_set_mute(pa_source *source, pa_bool_t mute); -pa_bool_t pa_source_get_mute(pa_source *source); +pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh);  unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */  unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */ +unsigned pa_source_check_suspend(pa_source *s); /* Returns how many streams are active that don't allow suspensions */  #define pa_source_get_state(s) ((pa_source_state_t) (s)->state)  /* To be called exclusively by the source driver, from IO context */ diff --git a/src/pulsecore/start-child.c b/src/pulsecore/start-child.c index 1661383d..7774bde6 100644 --- a/src/pulsecore/start-child.c +++ b/src/pulsecore/start-child.c @@ -66,11 +66,6 @@ int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid) {          return pipe_fds[0];      } else { -#ifdef __linux__ -        DIR* d; -#endif -        int max_fd, i; -          /* child */          pa_reset_priority(); @@ -87,48 +82,9 @@ int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid) {          pa_close(2);          pa_assert_se(open("/dev/null", O_WRONLY) == 2); -#ifdef __linux__ - -        if ((d = opendir("/proc/self/fd/"))) { - -            struct dirent *de; - -            while ((de = readdir(d))) { -                char *e = NULL; -                int fd; - -                if (de->d_name[0] == '.') -                    continue; - -                errno = 0; -                fd = strtol(de->d_name, &e, 10); -                pa_assert(errno == 0 && e && *e == 0); - -                if (fd >= 3 && dirfd(d) != fd) -                    pa_close(fd); -            } - -            closedir(d); -        } else { - -#endif - -            max_fd = 1024; - -#ifdef HAVE_SYS_RESOURCE_H -            { -                struct rlimit r; -                if (getrlimit(RLIMIT_NOFILE, &r) == 0) -                    max_fd = r.rlim_max; -            } -#endif - -            for (i = 3; i < max_fd; i++) -                pa_close(i); - -#ifdef __linux__ -        } -#endif +        pa_close_all(-1); +        pa_reset_sigs(-1); +        pa_unblock_sigs(-1);  #ifdef PR_SET_PDEATHSIG          /* On Linux we can use PR_SET_PDEATHSIG to have the helper @@ -139,16 +95,6 @@ int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid) {          prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);  #endif -#ifdef SIGPIPE -        /* Make sure that SIGPIPE kills the child process */ -        signal(SIGPIPE, SIG_DFL); -#endif - -#ifdef SIGTERM -        /* Make sure that SIGTERM kills the child process */ -        signal(SIGTERM, SIG_DFL); -#endif -          execl(name, name, argv1, NULL);          _exit(1);      } diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c index b59b6f49..540faef9 100644 --- a/src/pulsecore/strbuf.c +++ b/src/pulsecore/strbuf.c @@ -77,7 +77,7 @@ char *pa_strbuf_tostring(pa_strbuf *sb) {      pa_assert(sb); -    e = t = pa_xnew(char, sb->length+1); +    e = t = pa_xmalloc(sb->length+1);      for (c = sb->head; c; c = c->next) {          pa_assert((size_t) (e-t) <= sb->length); @@ -150,8 +150,8 @@ void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) {  /* Append a printf() style formatted string to the string buffer. */  /* The following is based on an example from the GNU libc documentation */ -int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) { -    int size = 100; +size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) { +    size_t size = 100;      struct chunk *c = NULL;      pa_assert(sb); @@ -168,14 +168,14 @@ int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {          CHUNK_TO_TEXT(c)[size-1] = 0;          va_end(ap); -        if (r > -1 && r < size) { -            c->length = r; +        if (r > -1 && (size_t) r < size) { +            c->length = (size_t) r;              append(sb, c); -            return r; +            return (size_t) r;          }          if (r > -1)    /* glibc 2.1 */ -            size = r+1; +            size = (size_t) r+1;          else           /* glibc 2.0 */              size *= 2;      } diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h index 24c876d5..ac68d7be 100644 --- a/src/pulsecore/strbuf.h +++ b/src/pulsecore/strbuf.h @@ -31,7 +31,7 @@ void pa_strbuf_free(pa_strbuf *sb);  char *pa_strbuf_tostring(pa_strbuf *sb);  char *pa_strbuf_tostring_free(pa_strbuf *sb); -int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...)  PA_GCC_PRINTF_ATTR(2,3); +size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...)  PA_GCC_PRINTF_ATTR(2,3);  void pa_strbuf_puts(pa_strbuf *sb, const char *t);  void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m); diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c index b0ed59ef..62a30144 100644 --- a/src/pulsecore/tagstruct.c +++ b/src/pulsecore/tagstruct.c @@ -154,7 +154,7 @@ void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {      extend(t, 5+length);      t->data[t->length] = PA_TAG_ARBITRARY; -    tmp = htonl(length); +    tmp = htonl((uint32_t) length);      memcpy(t->data+t->length+1, &tmp, 4);      if (length)          memcpy(t->data+t->length+5, p, length); @@ -165,7 +165,7 @@ void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b) {      pa_assert(t);      extend(t, 1); -    t->data[t->length] = b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE; +    t->data[t->length] = (uint8_t) (b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE);      t->length += 1;  } @@ -175,9 +175,9 @@ void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {      extend(t, 9);      t->data[t->length] = PA_TAG_TIMEVAL; -    tmp = htonl(tv->tv_sec); +    tmp = htonl((uint32_t) tv->tv_sec);      memcpy(t->data+t->length+1, &tmp, 4); -    tmp = htonl(tv->tv_usec); +    tmp = htonl((uint32_t) tv->tv_usec);      memcpy(t->data+t->length+5, &tmp, 4);      t->length += 9;  } @@ -228,7 +228,7 @@ void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) {      unsigned i;      pa_assert(t); -    extend(t, 2 + map->channels); +    extend(t, 2 + (size_t) map->channels);      t->data[t->length++] = PA_TAG_CHANNEL_MAP;      t->data[t->length++] = map->channels; @@ -435,9 +435,9 @@ int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) {          return -1;      memcpy(&tv->tv_sec, t->data+t->rindex+1, 4); -    tv->tv_sec = ntohl(tv->tv_sec); +    tv->tv_sec = (time_t) ntohl((uint32_t) tv->tv_sec);      memcpy(&tv->tv_usec, t->data+t->rindex+5, 4); -    tv->tv_usec = ntohl(tv->tv_usec); +    tv->tv_usec = (suseconds_t) ntohl((uint32_t) tv->tv_usec);      t->rindex += 9;      return 0;  } @@ -523,7 +523,7 @@ int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) {      for (i = 0; i < map->channels; i ++)          map->map[i] = (int8_t) t->data[t->rindex + 2 + i]; -    t->rindex += 2 + map->channels; +    t->rindex += 2 + (size_t) map->channels;      return 0;  } diff --git a/src/pulsecore/thread-posix.c b/src/pulsecore/thread-posix.c index 20ed16d9..ade398f9 100644 --- a/src/pulsecore/thread-posix.c +++ b/src/pulsecore/thread-posix.c @@ -41,6 +41,7 @@ struct pa_thread {      pa_thread_func_t thread_func;      void *userdata;      pa_atomic_t running; +    pa_bool_t joined;  };  struct pa_tls { @@ -82,6 +83,7 @@ pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) {      t = pa_xnew(pa_thread, 1);      t->thread_func = thread_func;      t->userdata = userdata; +    t->joined = FALSE;      pa_atomic_store(&t->running, 0);      if (pthread_create(&t->id, NULL, internal_thread_func, t) < 0) { @@ -115,7 +117,12 @@ void pa_thread_free(pa_thread *t) {  int pa_thread_join(pa_thread *t) {      pa_assert(t); +    pa_assert(t->thread_func); + +    if (t->joined) +        return -1; +    t->joined = TRUE;      return pthread_join(t->id, NULL);  } @@ -132,6 +139,7 @@ pa_thread* pa_thread_self(void) {      t->id = pthread_self();      t->thread_func = NULL;      t->userdata = NULL; +    t->joined = TRUE;      pa_atomic_store(&t->running, 2);      PA_STATIC_TLS_SET(current_thread, t); @@ -192,4 +200,3 @@ void *pa_tls_set(pa_tls *t, void *userdata) {      pa_assert_se(pthread_setspecific(t->key, userdata) == 0);      return r;  } - diff --git a/src/pulsecore/thread.h b/src/pulsecore/thread.h index f3aca13e..eabe9ba4 100644 --- a/src/pulsecore/thread.h +++ b/src/pulsecore/thread.h @@ -25,6 +25,7 @@  #include <pulse/def.h>  #include <pulsecore/once.h> +#include <pulsecore/core-util.h>  #ifndef PACKAGE  #error "Please include config.h before including this file!" @@ -69,6 +70,8 @@ void *pa_tls_set(pa_tls *t, void *userdata);      static void name##_tls_destructor(void) PA_GCC_DESTRUCTOR;          \      static void name##_tls_destructor(void) {                           \          static void (*_free_cb)(void*) = free_cb;                       \ +        if (!pa_in_valgrind())                                          \ +            return;                                                     \          if (!name##_tls.tls)                                            \              return;                                                     \          if (_free_cb) {                                                 \ @@ -86,7 +89,7 @@ void *pa_tls_set(pa_tls *t, void *userdata);      }                                                                   \      struct __stupid_useless_struct_to_allow_trailing_semicolon -#ifdef HAVE_TLS_BUILTIN +#ifdef SUPPORT_TLS___THREAD  /* An optimized version of the above that requires no dynamic   * allocation if the compiler supports __thread */  #define PA_STATIC_TLS_DECLARE_NO_FREE(name)                             \ diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c index d0231486..65621948 100644 --- a/src/pulsecore/time-smoother.c +++ b/src/pulsecore/time-smoother.c @@ -209,8 +209,8 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) {      i = s->history_idx;      for (j = s->n_history; j > 0; j--) { -        ax += s->history_x[i]; -        ay += s->history_y[i]; +        ax += (int64_t) s->history_x[i]; +        ay += (int64_t) s->history_y[i];          c++;          REDUCE_INC(i); @@ -236,7 +236,7 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) {          REDUCE_INC(i);      } -    r = (double) k / t; +    r = (double) k / (double) t;      return (s->monotonic && r < 0) ? 0 : r;  } @@ -268,8 +268,8 @@ static void calc_abc(pa_smoother *s) {      /* Calculate a, b, c for y=ax^3+bx^2+cx */      s->c = de; -    s->b = (((double) (3*ky)/kx - dp - 2*de)) / kx; -    s->a = (dp/kx - 2*s->b - de/kx) / (3*kx); +    s->b = (((double) (3*ky)/ (double) kx - dp - (double) (2*de))) / (double) kx; +    s->a = (dp/(double) kx - 2*s->b - de/(double) kx) / (double) (3*kx);      s->abc_valid = TRUE;  } @@ -284,7 +284,7 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {          /* The requested point is right of the point where we wanted           * to be on track again, thus just linearly estimate */ -        t = (int64_t) s->py + (int64_t) (s->dp * (x - s->px)); +        t = (int64_t) s->py + (int64_t) llrint(s->dp * (double) (x - s->px));          if (t < 0)              t = 0; @@ -313,7 +313,7 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {          /* Move back from origin */          ty += (double) s->ey; -        *y = ty >= 0 ? (pa_usec_t) ty : 0; +        *y = ty >= 0 ? (pa_usec_t) llrint(ty) : 0;          /* Horner scheme */          if (deriv) @@ -360,7 +360,7 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {      /* And calculate when we want to be on track again */      s->px = s->ex + s->adjust_time; -    s->py = s->ry + s->dp *s->adjust_time; +    s->py = s->ry + (pa_usec_t) llrint(s->dp * (double) s->adjust_time);      s->abc_valid = FALSE; @@ -456,7 +456,7 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay)  /*     pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde); */ -    return (pa_usec_t) ((double) y_delay / nde); +    return (pa_usec_t) llrint((double) y_delay / nde);  }  void pa_smoother_reset(pa_smoother *s) { diff --git a/src/pulsecore/tokenizer.c b/src/pulsecore/tokenizer.c index d1e0836b..07a9f3ac 100644 --- a/src/pulsecore/tokenizer.c +++ b/src/pulsecore/tokenizer.c @@ -34,7 +34,7 @@  #include "tokenizer.h" -static void token_free(void *p, PA_GCC_UNUSED void *userdata) { +static void token_free(void *p, void *userdata) {      pa_xfree(p);  } diff --git a/src/pulsecore/x11prop.c b/src/pulsecore/x11prop.c index 9e75f63a..7f91ba3c 100644 --- a/src/pulsecore/x11prop.c +++ b/src/pulsecore/x11prop.c @@ -32,7 +32,7 @@  void pa_x11_set_prop(Display *d, const char *name, const char *data) {      Atom a = XInternAtom(d, name, False); -    XChangeProperty(d, RootWindow(d, 0), a, XA_STRING, 8, PropModeReplace, (const unsigned char*) data, strlen(data)+1); +    XChangeProperty(d, RootWindow(d, 0), a, XA_STRING, 8, PropModeReplace, (const unsigned char*) data, (int) (strlen(data)+1));  }  void pa_x11_del_prop(Display *d, const char *name) { @@ -49,7 +49,7 @@ char* pa_x11_get_prop(Display *d, const char *name, char *p, size_t l) {      char *ret = NULL;      Atom a = XInternAtom(d, name, False); -    if (XGetWindowProperty(d, RootWindow(d, 0), a, 0, (l+2)/4, False, XA_STRING, &actual_type, &actual_format, &nitems, &nbytes_after, &prop) != Success) +    if (XGetWindowProperty(d, RootWindow(d, 0), a, 0, (long) ((l+2)/4), False, XA_STRING, &actual_type, &actual_format, &nitems, &nbytes_after, &prop) != Success)          goto finish;      if (actual_type != XA_STRING) diff --git a/src/pulsecore/x11wrap.c b/src/pulsecore/x11wrap.c index 17f8e6a4..332ebb2e 100644 --- a/src/pulsecore/x11wrap.c +++ b/src/pulsecore/x11wrap.c @@ -91,7 +91,7 @@ static void work(pa_x11_wrapper *w) {  }  /* IO notification event for the X11 display connection */ -static void display_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { +static void display_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {      pa_x11_wrapper *w = userdata;      pa_assert(m); @@ -118,7 +118,7 @@ static void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {  }  /* IO notification event for X11 internal connections */ -static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { +static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {      pa_x11_wrapper *w = userdata;      pa_assert(m); diff --git a/src/tests/channelmap-test.c b/src/tests/channelmap-test.c index 9c234602..6cf58fb0 100644 --- a/src/tests/channelmap-test.c +++ b/src/tests/channelmap-test.c @@ -1,10 +1,14 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +  #include <stdio.h>  #include <assert.h>  #include <pulse/channelmap.h>  #include <pulse/gccmacro.h> -int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { +int main(int argc, char *argv[]) {      char cm[PA_CHANNEL_MAP_SNPRINT_MAX];      pa_channel_map map, map2; diff --git a/src/tests/cpulimit-test.c b/src/tests/cpulimit-test.c index b7145e8a..fdc0162e 100644 --- a/src/tests/cpulimit-test.c +++ b/src/tests/cpulimit-test.c @@ -42,7 +42,7 @@ static time_t start;  #ifdef TEST2 -static void func(pa_mainloop_api *m, PA_GCC_UNUSED pa_signal_event *e, PA_GCC_UNUSED int sig, PA_GCC_UNUSED void *userdata) { +static void func(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {      time_t now;      time(&now); @@ -55,7 +55,7 @@ static void func(pa_mainloop_api *m, PA_GCC_UNUSED pa_signal_event *e, PA_GCC_UN  #endif -int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { +int main(int argc, char *argv[]) {      pa_mainloop *m;      m = pa_mainloop_new(); diff --git a/src/tests/envelope-test.c b/src/tests/envelope-test.c index 9f914553..4a72f5a3 100644 --- a/src/tests/envelope-test.c +++ b/src/tests/envelope-test.c @@ -40,7 +40,7 @@ const pa_envelope_def ramp_down = {      .n_points = 2,      .points_x = { 100*PA_USEC_PER_MSEC, 300*PA_USEC_PER_MSEC },      .points_y = { -        .f = { 1.0, 0.2 }, +        .f = { 1.0f, 0.2f },          .i = { 0x10000, 0x10000/5 }      }  }; @@ -49,7 +49,7 @@ const pa_envelope_def ramp_up = {      .n_points = 2,      .points_x = { 100*PA_USEC_PER_MSEC, 300*PA_USEC_PER_MSEC },      .points_y = { -        .f = { 0.2, 1.0 }, +        .f = { 0.2f, 1.0f },          .i = { 0x10000/5, 0x10000 }      }  }; @@ -58,7 +58,7 @@ const pa_envelope_def ramp_down2 = {      .n_points = 2,      .points_x = { 50*PA_USEC_PER_MSEC, 900*PA_USEC_PER_MSEC },      .points_y = { -        .f = { 0.8, 0.7 }, +        .f = { 0.8f, 0.7f },          .i = { 0x10000*4/5, 0x10000*7/10 }      }  }; @@ -67,7 +67,7 @@ const pa_envelope_def ramp_up2 = {      .n_points = 2,      .points_x = { 50*PA_USEC_PER_MSEC, 900*PA_USEC_PER_MSEC },      .points_y = { -        .f = { 0.7, 0.9 }, +        .f = { 0.7f, 0.9f },          .i = { 0x10000*7/10, 0x10000*9/10 }      }  }; @@ -140,7 +140,7 @@ static pa_memblock * generate_block(pa_mempool *pool, const pa_sample_spec *ss)      unsigned n_samples;      block = pa_memblock_new(pool, pa_bytes_per_second(ss)); -    n_samples = pa_memblock_get_length(block) / pa_sample_size(ss); +    n_samples = (unsigned) (pa_memblock_get_length(block) / pa_sample_size(ss));      d = pa_memblock_acquire(block); @@ -171,7 +171,7 @@ static pa_memblock * generate_block(pa_mempool *pool, const pa_sample_spec *ss)              float *f;              for (f = d; n_samples > 0; n_samples--, f++) -                *f = PA_MAYBE_FLOAT32_SWAP(ss->format == PA_SAMPLE_FLOAT32RE, 1.0); +                *f = PA_MAYBE_FLOAT32_SWAP(ss->format == PA_SAMPLE_FLOAT32RE, 1.0f);              break;          } @@ -205,7 +205,7 @@ int main(int argc, char *argv[]) {      oil_init();      pa_log_set_maximal_level(PA_LOG_DEBUG); -    pa_assert_se(pool = pa_mempool_new(FALSE)); +    pa_assert_se(pool = pa_mempool_new(FALSE, 0));      pa_assert_se(envelope = pa_envelope_new(&ss));      block = generate_block(pool, &ss); diff --git a/src/tests/lock-autospawn-test.c b/src/tests/lock-autospawn-test.c new file mode 100644 index 00000000..80cfda6a --- /dev/null +++ b/src/tests/lock-autospawn-test.c @@ -0,0 +1,109 @@ +/*** +  This file is part of PulseAudio. + +  Copyright 2008 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 <sys/poll.h> +#include <string.h> + +#include <pulsecore/macro.h> +#include <pulsecore/thread.h> +#include <pulsecore/lock-autospawn.h> +#include <pulse/util.h> + +static void thread_func(void*k) { +    pa_assert_se(pa_autospawn_lock_init() >= 0); + +    pa_log("%i, Trying to acquire lock.", PA_PTR_TO_INT(k)); + +    pa_assert_se(pa_autospawn_lock_acquire(TRUE) > 0); + +    pa_log("%i, Got the lock!, Sleeping for 5s", PA_PTR_TO_INT(k)); + +    pa_msleep(5000); + +    pa_log("%i, Releasing", PA_PTR_TO_INT(k)); + +    pa_autospawn_lock_release(); + +    pa_autospawn_lock_done(FALSE); +} + +static void thread_func2(void *k) { +    int fd; + +    pa_assert_se((fd = pa_autospawn_lock_init()) >= 0); + +    pa_log("%i, Trying to acquire lock.", PA_PTR_TO_INT(k)); + +    for (;;) { +        struct pollfd pollfd; +        int j; + +        if ((j = pa_autospawn_lock_acquire(FALSE)) > 0) +            break; + +        pa_assert(j == 0); + +        memset(&pollfd, 0, sizeof(pollfd)); +        pollfd.fd = fd; +        pollfd.events = POLLIN; + +        pa_assert_se(poll(&pollfd, 1, -1) == 1); + +        pa_log("%i, woke up", PA_PTR_TO_INT(k)); +    } + +    pa_log("%i, Got the lock!, Sleeping for 5s", PA_PTR_TO_INT(k)); + +    pa_msleep(5000); + +    pa_log("%i, Releasing", PA_PTR_TO_INT(k)); + +    pa_autospawn_lock_release(); + +    pa_autospawn_lock_done(FALSE); +} + +int main(int argc, char**argv) { +    pa_thread *a, *b, *c, *d; + +    pa_assert_se((a = pa_thread_new(thread_func, PA_INT_TO_PTR(1)))); +    pa_assert_se((b = pa_thread_new(thread_func2, PA_INT_TO_PTR(2)))); +    pa_assert_se((c = pa_thread_new(thread_func2, PA_INT_TO_PTR(3)))); +    pa_assert_se((d = pa_thread_new(thread_func, PA_INT_TO_PTR(4)))); + +    pa_thread_join(a); +    pa_thread_join(b); +    pa_thread_join(c); +    pa_thread_join(d); + +    pa_thread_free(a); +    pa_thread_free(b); +    pa_thread_free(c); +    pa_thread_free(d); + +    pa_log("End"); + +    return 0; +} diff --git a/src/tests/mainloop-test.c b/src/tests/mainloop-test.c index 9fa2e466..2580fa72 100644 --- a/src/tests/mainloop-test.c +++ b/src/tests/mainloop-test.c @@ -66,7 +66,7 @@ static void tcb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, v  #endif  } -int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { +int main(int argc, char *argv[]) {      pa_mainloop_api *a;      pa_io_event *ioe;      pa_time_event *te; diff --git a/src/tests/mcalign-test.c b/src/tests/mcalign-test.c index 9e358359..92e3e14e 100644 --- a/src/tests/mcalign-test.c +++ b/src/tests/mcalign-test.c @@ -36,18 +36,18 @@  /* A simple program for testing pa_mcalign */ -int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { +int main(int argc, char *argv[]) {      pa_mempool *p;      pa_mcalign *a;      pa_memchunk c; -    p = pa_mempool_new(0); +    p = pa_mempool_new(FALSE, 0);      a = pa_mcalign_new(11);      pa_memchunk_reset(&c); -    srand(time(NULL)); +    srand((unsigned) time(NULL));      for (;;) {          ssize_t r; @@ -62,7 +62,7 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {          l = pa_memblock_get_length(c.memblock) - c.index; -        l = l <= 1 ? l : rand() % (l-1) +1 ; +        l = l <= 1 ? l : (size_t) rand() % (l-1) +1;          p = pa_memblock_acquire(c.memblock); @@ -74,11 +74,11 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {          pa_memblock_release(c.memblock); -        c.length = r; +        c.length = (size_t) r;          pa_mcalign_push(a, &c);          fprintf(stderr, "Read %ld bytes\n", (long)r); -        c.index += r; +        c.index += (size_t) r;          if (c.index >= pa_memblock_get_length(c.memblock)) {              pa_memblock_unref(c.memblock); diff --git a/src/tests/memblock-test.c b/src/tests/memblock-test.c index 6da1b1e9..37b5b403 100644 --- a/src/tests/memblock-test.c +++ b/src/tests/memblock-test.c @@ -78,9 +78,9 @@ int main(int argc, char *argv[]) {      const char txt[] = "This is a test!"; -    pool_a = pa_mempool_new(1); -    pool_b = pa_mempool_new(1); -    pool_c = pa_mempool_new(1); +    pool_a = pa_mempool_new(TRUE, 0); +    pool_b = pa_mempool_new(TRUE, 0); +    pool_c = pa_mempool_new(TRUE, 0);      pa_mempool_get_shm_id(pool_a, &id_a);      pa_mempool_get_shm_id(pool_b, &id_b); diff --git a/src/tests/memblockq-test.c b/src/tests/memblockq-test.c index 7bf992a1..c53945b4 100644 --- a/src/tests/memblockq-test.c +++ b/src/tests/memblockq-test.c @@ -63,7 +63,7 @@ int main(int argc, char *argv[]) {      pa_log_set_maximal_level(PA_LOG_DEBUG); -    p = pa_mempool_new(0); +    p = pa_mempool_new(FALSE, 0);      silence.memblock = pa_memblock_new_fixed(p, (char*)  "__", 2, 1);      assert(silence.memblock); diff --git a/src/tests/mix-test.c b/src/tests/mix-test.c index f3f6f829..cc21ab03 100644 --- a/src/tests/mix-test.c +++ b/src/tests/mix-test.c @@ -166,16 +166,16 @@ static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {          case PA_SAMPLE_FLOAT32RE: {              float *u = d; -            u[0] = 0.0; -            u[1] = -1.0; -            u[2] = 1.0; -            u[3] = 4711; -            u[4] = 0.222; -            u[5] = 0.33; -            u[6] = -.3; -            u[7] = 99; -            u[8] = -0.555; -            u[9] = -.123; +            u[0] = 0.0f; +            u[1] = -1.0f; +            u[2] = 1.0f; +            u[3] = 4711.0f; +            u[4] = 0.222f; +            u[5] = 0.33f; +            u[6] = -.3f; +            u[7] = 99.0f; +            u[8] = -0.555f; +            u[9] = -.123f;              if (ss->format == PA_SAMPLE_FLOAT32RE)                  for (i = 0; i < 10; i++) @@ -201,7 +201,7 @@ int main(int argc, char *argv[]) {      oil_init();      pa_log_set_maximal_level(PA_LOG_DEBUG); -    pa_assert_se(pool = pa_mempool_new(FALSE)); +    pa_assert_se(pool = pa_mempool_new(FALSE, 0));      a.channels = 1;      a.rate = 44100; @@ -221,6 +221,8 @@ int main(int argc, char *argv[]) {          i.length = pa_memblock_get_length(i.memblock);          i.index = 0; +        dump_block(&a, &i); +          /* Make a copy */          j = i;          pa_memblock_ref(j.memblock); @@ -229,6 +231,8 @@ int main(int argc, char *argv[]) {          /* Adjust volume of the copy */          pa_volume_memchunk(&j, &a, &v); +        dump_block(&a, &j); +          m[0].chunk = i;          m[0].volume.values[0] = PA_VOLUME_NORM;          m[0].volume.channels = a.channels; @@ -244,8 +248,6 @@ int main(int argc, char *argv[]) {          pa_mix(m, 2, ptr, k.length, &a, NULL, FALSE);          pa_memblock_release(k.memblock); -        dump_block(&a, &i); -        dump_block(&a, &j);          dump_block(&a, &k);          pa_memblock_unref(i.memblock); diff --git a/src/tests/pacat-simple.c b/src/tests/pacat-simple.c index b26e4b68..ffe3176a 100644 --- a/src/tests/pacat-simple.c +++ b/src/tests/pacat-simple.c @@ -33,7 +33,7 @@  #define BUFSIZE 1024 -int main(PA_GCC_UNUSED int argc, char*argv[]) { +int main(int argc, char*argv[]) {      /* The Sample format to use */      static const pa_sample_spec ss = { @@ -94,7 +94,7 @@ int main(PA_GCC_UNUSED int argc, char*argv[]) {          }          /* ... and play it */ -        if (pa_simple_write(s, buf, r, &error) < 0) { +        if (pa_simple_write(s, buf, (size_t) r, &error) < 0) {              fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error));              goto finish;          } diff --git a/src/tests/parec-simple.c b/src/tests/parec-simple.c index 6c0d529b..c9d3bef5 100644 --- a/src/tests/parec-simple.c +++ b/src/tests/parec-simple.c @@ -47,13 +47,13 @@ static ssize_t loop_write(int fd, const void*data, size_t size) {          ret += r;          data = (const uint8_t*) data + r; -        size -= r; +        size -= (size_t) r;      }      return ret;  } -int main(PA_GCC_UNUSED int argc, char*argv[]) { +int main(int argc, char*argv[]) {      /* The sample type to use */      static const pa_sample_spec ss = {          .format = PA_SAMPLE_S16LE, @@ -72,7 +72,6 @@ int main(PA_GCC_UNUSED int argc, char*argv[]) {      for (;;) {          uint8_t buf[BUFSIZE]; -        ssize_t r;          /* Record some data ... */          if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) { @@ -81,7 +80,7 @@ int main(PA_GCC_UNUSED int argc, char*argv[]) {          }          /* And write it to STDOUT */ -        if ((r = loop_write(STDOUT_FILENO, buf, sizeof(buf))) <= 0) { +        if (loop_write(STDOUT_FILENO, buf, sizeof(buf)) != sizeof(buf)) {              fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));              goto finish;          } diff --git a/src/tests/prioq-test.c b/src/tests/prioq-test.c new file mode 100644 index 00000000..120b512b --- /dev/null +++ b/src/tests/prioq-test.c @@ -0,0 +1,44 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pulsecore/prioq.h> +#include <pulsecore/macro.h> + +#define N 1024 + +int main(int argc, char *argv[]) { +    pa_prioq *q; +    unsigned i; + +    srand(0); + +    q = pa_prioq_new(pa_idxset_trivial_compare_func); + +    /* Fill in 1024 */ +    for (i = 0; i < N; i++) +        pa_prioq_put(q, PA_UINT_TO_PTR((unsigned) rand())); + +    /* Remove half of it again */ +    for (i = 0; i < N/2; i++){ +        unsigned u = PA_PTR_TO_UINT(pa_prioq_pop(q)); +        pa_log("%16u", u); +    } + +    pa_log("Refilling"); + +    /* Fill in another 1024 */ +    for (i = 0; i < N; i++) +        pa_prioq_put(q, PA_UINT_TO_PTR((unsigned) rand())); + + +    /* Remove everything */ +    while (!pa_prioq_isempty(q)) { +        unsigned u = PA_PTR_TO_UINT(pa_prioq_pop(q)); +        pa_log("%16u", u); +    } + +    pa_prioq_free(q, NULL, NULL); + +    return 0; +} diff --git a/src/tests/remix-test.c b/src/tests/remix-test.c index 4777c150..3538d7d4 100644 --- a/src/tests/remix-test.c +++ b/src/tests/remix-test.c @@ -58,7 +58,7 @@ int main(int argc, char *argv[]) {      oil_init();      pa_log_set_maximal_level(PA_LOG_DEBUG); -    pa_assert_se(pool = pa_mempool_new(FALSE)); +    pa_assert_se(pool = pa_mempool_new(FALSE, 0));      for (i = 0; maps[i].channels > 0; i++)          for (j = 0; maps[j].channels > 0; j++) { diff --git a/src/tests/resampler-test.c b/src/tests/resampler-test.c index 1a20be2c..2d591867 100644 --- a/src/tests/resampler-test.c +++ b/src/tests/resampler-test.c @@ -166,16 +166,16 @@ static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {          case PA_SAMPLE_FLOAT32RE: {              float *u = d; -            u[0] = 0.0; -            u[1] = -1.0; -            u[2] = 1.0; -            u[3] = 4711; -            u[4] = 0.222; -            u[5] = 0.33; -            u[6] = -.3; -            u[7] = 99; -            u[8] = -0.555; -            u[9] = -.123; +            u[0] = 0.0f; +            u[1] = -1.0f; +            u[2] = 1.0f; +            u[3] = 4711.0f; +            u[4] = 0.222f; +            u[5] = 0.33f; +            u[6] = -.3f; +            u[7] = 99.0f; +            u[8] = -0.555f; +            u[9] = -.123f;              if (ss->format == PA_SAMPLE_FLOAT32RE)                  for (i = 0; i < 10; i++) @@ -201,7 +201,7 @@ int main(int argc, char *argv[]) {      oil_init();      pa_log_set_maximal_level(PA_LOG_DEBUG); -    pa_assert_se(pool = pa_mempool_new(FALSE)); +    pa_assert_se(pool = pa_mempool_new(FALSE, 0));      a.channels = b.channels = 1;      a.rate = b.rate = 44100; diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c index 91e85c36..6b0cb8f7 100644 --- a/src/tests/rtstutter.c +++ b/src/tests/rtstutter.c @@ -52,7 +52,7 @@ static void* work(void *p) {      pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m) == 0);      CPU_ZERO(&mask); -    CPU_SET(PA_PTR_TO_INT(p), &mask); +    CPU_SET((size_t) PA_PTR_TO_INT(p), &mask);      pa_assert_se(pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) == 0);      for (;;) { @@ -65,17 +65,17 @@ static void* work(void *p) {          pa_assert_se(clock_gettime(CLOCK_REALTIME, &end) == 0);          nsec = -            (uint64_t) ((((double) rand())*(msec_upper-msec_lower)*PA_NSEC_PER_MSEC)/RAND_MAX) + -            (uint64_t) (msec_lower*PA_NSEC_PER_MSEC); +            (uint64_t) ((((double) rand())*(double)(msec_upper-msec_lower)*PA_NSEC_PER_MSEC)/RAND_MAX) + +            (uint64_t) ((uint64_t) msec_lower*PA_NSEC_PER_MSEC);          pa_log_notice("CPU%i: Freezing for %ims", PA_PTR_TO_INT(p), (int) (nsec/PA_NSEC_PER_MSEC)); -        end.tv_sec += nsec / PA_NSEC_PER_SEC; -        end.tv_nsec += nsec % PA_NSEC_PER_SEC; +        end.tv_sec += (time_t) (nsec / PA_NSEC_PER_SEC); +        end.tv_nsec += (long int) (nsec % PA_NSEC_PER_SEC);          while ((pa_usec_t) end.tv_nsec > PA_NSEC_PER_SEC) {              end.tv_sec++; -            end.tv_nsec -= PA_NSEC_PER_SEC; +            end.tv_nsec -= (long int) PA_NSEC_PER_SEC;          }          do { @@ -88,7 +88,7 @@ static void* work(void *p) {  int main(int argc, char*argv[]) {      int n; -    srand(time(NULL)); +    srand((unsigned) time(NULL));      if (argc >= 3) {          msec_lower = atoi(argv[1]); diff --git a/src/tests/smoother-test.c b/src/tests/smoother-test.c index b78f3c91..15700ec2 100644 --- a/src/tests/smoother-test.c +++ b/src/tests/smoother-test.c @@ -64,7 +64,7 @@ int main(int argc, char*argv[]) {      for (x = 0, u = 0; x < PA_USEC_PER_SEC * 10; x += PA_USEC_PER_MSEC) {          while (u < PA_ELEMENTSOF(msec) && (pa_usec_t) msec[u]*PA_USEC_PER_MSEC < x) { -            pa_smoother_put(s, msec[u]*PA_USEC_PER_MSEC, msec[u+1]*PA_USEC_PER_MSEC); +            pa_smoother_put(s, (pa_usec_t) msec[u] * PA_USEC_PER_MSEC, (pa_usec_t) msec[u+1] * PA_USEC_PER_MSEC);              printf("%i\t\t%i\n", msec[u],  msec[u+1]);              u += 2;          } diff --git a/src/tests/stripnul.c b/src/tests/stripnul.c index 0ab06776..2b8aa083 100644 --- a/src/tests/stripnul.c +++ b/src/tests/stripnul.c @@ -35,7 +35,7 @@ int main(int argc, char *argv[]) {      uint8_t *zero;      pa_assert_se(argc >= 2); -    pa_assert_se((granularity = atoi(argv[1])) >= 1); +    pa_assert_se((granularity = (size_t) atoi(argv[1])) >= 1);      pa_assert_se((i = (argc >= 3) ? fopen(argv[2], "r") : stdin));      pa_assert_se((o = (argc >= 4) ? fopen(argv[3], "w") : stdout)); @@ -53,11 +53,11 @@ int main(int argc, char *argv[]) {          if (found)              pa_assert_se(fwrite(buffer, granularity, k, o) == k);          else { -            for (p = buffer; (p-buffer)/granularity < k; p += granularity) +            for (p = buffer; ((size_t) (p-buffer)/granularity) < k; p += granularity)                  if (memcmp(p, zero, granularity)) {                      size_t left;                      found = TRUE; -                    left = k - (p-buffer)/granularity; +                    left = (size_t) (k - (size_t) (p-buffer)/granularity);                      pa_assert_se(fwrite(p, granularity, left, o) == left);                      break;                  } diff --git a/src/tests/strlist-test.c b/src/tests/strlist-test.c index 2bd1645c..10f370c2 100644 --- a/src/tests/strlist-test.c +++ b/src/tests/strlist-test.c @@ -5,7 +5,7 @@  #include <pulsecore/strlist.h> -int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char* argv[]) { +int main(int argc, char* argv[]) {      char *t, *u;      pa_strlist *l = NULL; diff --git a/src/tests/sync-playback.c b/src/tests/sync-playback.c index 7e364685..42c479a1 100644 --- a/src/tests/sync-playback.c +++ b/src/tests/sync-playback.c @@ -89,7 +89,7 @@ static void stream_state_callback(pa_stream *s, void *userdata) {              fprintf(stderr, "Writing data to stream %i.\n", i); -            r = pa_stream_write(s, data, sizeof(data), nop_free_cb, sizeof(data) * i, PA_SEEK_ABSOLUTE); +            r = pa_stream_write(s, data, sizeof(data), nop_free_cb, (int64_t) sizeof(data) * (int64_t) i, PA_SEEK_ABSOLUTE);              assert(r == 0);              /* Be notified when this stream is drained */ diff --git a/src/tests/thread-mainloop-test.c b/src/tests/thread-mainloop-test.c index 7a62f85a..263cd57d 100644 --- a/src/tests/thread-mainloop-test.c +++ b/src/tests/thread-mainloop-test.c @@ -39,7 +39,7 @@ static void tcb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, v      fprintf(stderr, "TIME EVENT END\n");  } -int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { +int main(int argc, char *argv[]) {      pa_mainloop_api *a;      pa_threaded_mainloop *m;      struct timeval tv; diff --git a/src/tests/voltest.c b/src/tests/voltest.c index d2c0ff69..5bfc97e0 100644 --- a/src/tests/voltest.c +++ b/src/tests/voltest.c @@ -3,8 +3,9 @@  #include <pulse/volume.h>  #include <pulse/gccmacro.h> -int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { +int main(int argc, char *argv[]) {      pa_volume_t v; +    pa_cvolume cv;      for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 256) { @@ -13,6 +14,17 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {          printf("Volume: %3i; percent: %i%%; decibel %0.2f; linear = %0.2f; volume(decibel): %3i; volume(linear): %3i\n",                 v, (v*100)/PA_VOLUME_NORM, dB, f, pa_sw_volume_from_dB(dB), pa_sw_volume_from_linear(f)); +    } + +    for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 256) { +        char s[PA_CVOLUME_SNPRINT_MAX], t[PA_SW_CVOLUME_SNPRINT_DB_MAX]; + +        pa_cvolume_set(&cv, 2, v); + +        printf("Volume: %3i [%s] [%s]\n", +               v, +               pa_cvolume_snprint(s, sizeof(s), &cv), +               pa_sw_cvolume_snprint_dB(t, sizeof(t), &cv));      } diff --git a/src/utils/pacat.c b/src/utils/pacat.c index 32fa6bcf..ea736e23 100644 --- a/src/utils/pacat.c +++ b/src/utils/pacat.c @@ -57,6 +57,7 @@ static char *stream_name = NULL, *client_name = NULL, *device = NULL;  static int verbose = 0;  static pa_volume_t volume = PA_VOLUME_NORM; +static int volume_is_set = 0;  static pa_sample_spec sample_spec = {      .format = PA_SAMPLE_S16LE, @@ -274,8 +275,8 @@ static void context_state_callback(pa_context *c, void *userdata) {              if (latency > 0) {                  memset(&buffer_attr, 0, sizeof(buffer_attr)); -                buffer_attr.tlength = latency; -                buffer_attr.minreq = process_time; +                buffer_attr.tlength = (uint32_t) latency; +                buffer_attr.minreq = (uint32_t) process_time;                  buffer_attr.maxlength = (uint32_t) -1;                  buffer_attr.prebuf = (uint32_t) -1;                  flags |= PA_STREAM_ADJUST_LATENCY; @@ -283,7 +284,7 @@ static void context_state_callback(pa_context *c, void *userdata) {              if (mode == PLAYBACK) {                  pa_cvolume cv; -                if ((r = pa_stream_connect_playback(stream, device, latency > 0 ? &buffer_attr : NULL, flags, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL)) < 0) { +                if ((r = pa_stream_connect_playback(stream, device, latency > 0 ? &buffer_attr : NULL, flags, volume_is_set ? pa_cvolume_set(&cv, sample_spec.channels, volume) : NULL, NULL)) < 0) {                      fprintf(stderr, _("pa_stream_connect_playback() failed: %s\n"), pa_strerror(pa_context_errno(c)));                      goto fail;                  } @@ -391,7 +392,7 @@ static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_even          return;      } -    buffer_length = r; +    buffer_length = (uint32_t) r;      buffer_index = 0;      if (w) @@ -422,8 +423,8 @@ static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_eve          return;      } -    buffer_length -= r; -    buffer_index += r; +    buffer_length -= (uint32_t) r; +    buffer_index += (uint32_t) r;      if (!buffer_length) {          pa_xfree(buffer); @@ -456,7 +457,7 @@ static void stream_update_timing_callback(pa_stream *s, int success, void *userd      fprintf(stderr, _("Time: %0.3f sec; Latency: %0.0f usec.  \r"),              (float) usec / 1000000, -            (float) l * (negative?-1:1)); +            (float) l * (negative?-1.0f:1.0f));  }  /* Someone requested that the latency is shown */ @@ -500,7 +501,7 @@ static void help(const char *argv0) {             "      --volume=VOLUME                   Specify the initial (linear) volume in range 0...65536\n"             "      --rate=SAMPLERATE                 The sample rate in Hz (defaults to 44100)\n"             "      --format=SAMPLEFORMAT             The sample type, one of s16le, s16be, u8, float32le,\n" -           "                                        float32be, ulaw, alaw (defaults to s16ne)\n" +           "                                        float32be, ulaw, alaw, s32le, s32be (defaults to s16ne)\n"             "      --channels=CHANNELS               The number of channels, 1 for mono, 2 for stereo\n"             "                                        (defaults to 2)\n"             "      --channel-map=CHANNELMAP          Channel map to use instead of the default\n" @@ -626,12 +627,13 @@ int main(int argc, char *argv[]) {              case ARG_VOLUME: {                  int v = atoi(optarg); -                volume = v < 0 ? 0 : v; +                volume = v < 0 ? 0U : (pa_volume_t) v; +                volume_is_set = 1;                  break;              }              case ARG_CHANNELS: -                sample_spec.channels = atoi(optarg); +                sample_spec.channels = (uint8_t) atoi(optarg);                  break;              case ARG_SAMPLEFORMAT: @@ -639,7 +641,7 @@ int main(int argc, char *argv[]) {                  break;              case ARG_SAMPLERATE: -                sample_spec.rate = atoi(optarg); +                sample_spec.rate = (uint32_t) atoi(optarg);                  break;              case ARG_CHANNELMAP: @@ -672,14 +674,14 @@ int main(int argc, char *argv[]) {                  break;              case ARG_LATENCY: -                if (((latency = atoi(optarg))) <= 0) { +                if (((latency = (size_t) atoi(optarg))) <= 0) {                      fprintf(stderr, _("Invalid latency specification '%s'\n"), optarg);                      goto quit;                  }                  break;              case ARG_PROCESS_TIME: -                if (((process_time = atoi(optarg))) <= 0) { +                if (((process_time = (size_t) atoi(optarg))) <= 0) {                      fprintf(stderr, _("Invalid process time specification '%s'\n"), optarg);                      goto quit;                  } @@ -695,7 +697,7 @@ int main(int argc, char *argv[]) {          goto quit;      } -    if (channel_map_set && channel_map.channels != sample_spec.channels) { +    if (channel_map_set && pa_channel_map_compatible(&channel_map, &sample_spec)) {          fprintf(stderr, _("Channel map doesn't match sample specification\n"));          goto quit;      } @@ -773,7 +775,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; +    }      if (verbose) {          struct timeval tv; diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c index 24cddaa3..2c89c8d9 100644 --- a/src/utils/pacmd.c +++ b/src/utils/pacmd.c @@ -42,7 +42,7 @@  #include <pulsecore/log.h>  #include <pulsecore/pid.h> -int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) { +int main(int argc, char*argv[]) {      pid_t pid ;      int fd = -1;      int ret = 1, i; diff --git a/src/utils/pactl.c b/src/utils/pactl.c index 3f00df1b..2f430ca7 100644 --- a/src/utils/pactl.c +++ b/src/utils/pactl.c @@ -555,7 +555,7 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {      d = pa_xmalloc(length);      assert(sample_length >= length); -    l = length/pa_frame_size(&sample_spec); +    l = (sf_count_t) (length/pa_frame_size(&sample_spec));      if ((sf_readf_float(sndfile, d, l)) != l) {          pa_xfree(d); @@ -791,11 +791,11 @@ int main(int argc, char *argv[]) {                  goto quit;              } -            sample_spec.format =  PA_SAMPLE_FLOAT32; -            sample_spec.rate = sfinfo.samplerate; -            sample_spec.channels = sfinfo.channels; +            sample_spec.format = PA_SAMPLE_FLOAT32; +            sample_spec.rate = (uint32_t) sfinfo.samplerate; +            sample_spec.channels = (uint8_t) sfinfo.channels; -            sample_length = sfinfo.frames*pa_frame_size(&sample_spec); +            sample_length = (size_t)sfinfo.frames*pa_frame_size(&sample_spec);          } else if (!strcmp(argv[optind], "play-sample")) {              action = PLAY_SAMPLE;              if (argc != optind+2 && argc != optind+3) { @@ -823,7 +823,7 @@ int main(int argc, char *argv[]) {                  goto quit;              } -            sink_input_idx = atoi(argv[optind+1]); +            sink_input_idx = (uint32_t) atoi(argv[optind+1]);              sink_name = pa_xstrdup(argv[optind+2]);          } else if (!strcmp(argv[optind], "move-source-output")) {              action = MOVE_SOURCE_OUTPUT; @@ -832,7 +832,7 @@ int main(int argc, char *argv[]) {                  goto quit;              } -            source_output_idx = atoi(argv[optind+1]); +            source_output_idx = (uint32_t) atoi(argv[optind+1]);              source_name = pa_xstrdup(argv[optind+2]);          } else if (!strcmp(argv[optind], "load-module")) {              int i; @@ -852,7 +852,7 @@ int main(int argc, char *argv[]) {                  n += strlen(argv[i])+1;              if (n > 0) { -                p = module_args = pa_xnew0(char, n); +                p = module_args = pa_xmalloc(n);                  for (i = optind+2; i < argc; i++)                      p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]); @@ -866,7 +866,7 @@ int main(int argc, char *argv[]) {                  goto quit;              } -            module_index = atoi(argv[optind+1]); +            module_index = (uint32_t) atoi(argv[optind+1]);          } else if (!strcmp(argv[optind], "suspend-sink")) {              action = SUSPEND_SINK; diff --git a/src/utils/padsp.c b/src/utils/padsp.c index c82fde64..2e6e5575 100644 --- a/src/utils/padsp.c +++ b/src/utils/padsp.c @@ -422,7 +422,7 @@ static void fd_info_unref(fd_info *i) {      pthread_mutex_lock(&i->mutex);      assert(i->ref >= 1);      r = --i->ref; -        debug(DEBUG_LEVEL_VERBOSE, __FILE__": ref--, now %i\n", i->ref); +    debug(DEBUG_LEVEL_VERBOSE, __FILE__": ref--, now %i\n", i->ref);      pthread_mutex_unlock(&i->mutex);      if (r <= 0) @@ -498,7 +498,6 @@ static void atfork_prepare(void) {      pthread_mutex_lock(&func_mutex); -      debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_prepare() exit\n");  } @@ -550,12 +549,14 @@ static void atfork_child(void) {          }          if (i->app_fd >= 0) { -            close(i->app_fd); +            LOAD_CLOSE_FUNC(); +            _close(i->app_fd);              i->app_fd = -1;          }          if (i->thread_fd >= 0) { -            close(i->thread_fd); +            LOAD_CLOSE_FUNC(); +            _close(i->thread_fd);              i->thread_fd = -1;          } @@ -748,7 +749,7 @@ static void fix_metrics(fd_info *i) {      /* Number of fragments set? */      if (i->n_fragments < 2) {          if (i->fragment_size > 0) { -            i->n_fragments = pa_bytes_per_second(&i->sample_spec) / 2 / i->fragment_size; +            i->n_fragments = (unsigned) (pa_bytes_per_second(&i->sample_spec) / 2 / i->fragment_size);              if (i->n_fragments < 2)                  i->n_fragments = 2;          } else @@ -864,7 +865,7 @@ static int fd_info_copy_data(fd_info *i, int force) {                  return -1;              } -            if (pa_stream_write(i->play_stream, i->buf, r, free, 0, PA_SEEK_RELATIVE) < 0) { +            if (pa_stream_write(i->play_stream, i->buf, (size_t) r, free, 0LL, PA_SEEK_RELATIVE) < 0) {                  debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_write(): %s\n", pa_strerror(pa_context_errno(i->context)));                  return -1;              } @@ -872,7 +873,7 @@ static int fd_info_copy_data(fd_info *i, int force) {              i->buf = NULL;              assert(n >= (size_t) r); -            n -= r; +            n -= (size_t) r;          }          if (n >= i->fragment_size) @@ -916,7 +917,7 @@ static int fd_info_copy_data(fd_info *i, int force) {              }              assert((size_t)r <= len - i->rec_offset); -            i->rec_offset += r; +            i->rec_offset += (size_t) r;              if (i->rec_offset == len) {                  if (pa_stream_drop(i->rec_stream) < 0) { @@ -927,7 +928,7 @@ static int fd_info_copy_data(fd_info *i, int force) {              }              assert(n >= (size_t) r); -            n -= r; +            n -= (size_t) r;          }          if (n >= i->fragment_size) @@ -943,6 +944,10 @@ static int fd_info_copy_data(fd_info *i, int force) {          api->io_enable(i->io_event, i->io_flags);      } +    /* So, we emptied the socket now, let's tell dsp_empty_socket() +     * about this */ +    pa_threaded_mainloop_signal(i->mainloop, 0); +      return 0;  } @@ -998,12 +1003,12 @@ static int create_playback_stream(fd_info *i) {      pa_stream_set_latency_update_callback(i->play_stream, stream_latency_update_cb, i);      memset(&attr, 0, sizeof(attr)); -    attr.maxlength = i->fragment_size * (i->n_fragments+1); -    attr.tlength = i->fragment_size * i->n_fragments; -    attr.prebuf = i->fragment_size; -    attr.minreq = i->fragment_size; +    attr.maxlength = (uint32_t) (i->fragment_size * (i->n_fragments+1)); +    attr.tlength = (uint32_t) (i->fragment_size * i->n_fragments); +    attr.prebuf = (uint32_t) i->fragment_size; +    attr.minreq = (uint32_t) i->fragment_size; -    flags = PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE; +    flags = PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_EARLY_REQUESTS;      if (i->play_precork) {          flags |= PA_STREAM_START_CORKED;          debug(DEBUG_LEVEL_NORMAL, __FILE__": creating stream corked\n"); @@ -1013,9 +1018,9 @@ static int create_playback_stream(fd_info *i) {          goto fail;      } -    n = i->fragment_size; +    n = (int) i->fragment_size;      setsockopt(i->app_fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)); -    n = i->fragment_size; +    n = (int) i->fragment_size;      setsockopt(i->thread_fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));      return 0; @@ -1042,8 +1047,8 @@ static int create_record_stream(fd_info *i) {      pa_stream_set_latency_update_callback(i->rec_stream, stream_latency_update_cb, i);      memset(&attr, 0, sizeof(attr)); -    attr.maxlength = i->fragment_size * (i->n_fragments+1); -    attr.fragsize = i->fragment_size; +    attr.maxlength = (uint32_t) (i->fragment_size * (i->n_fragments+1)); +    attr.fragsize = (uint32_t) i->fragment_size;      flags = PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE;      if (i->rec_precork) { @@ -1055,9 +1060,9 @@ static int create_record_stream(fd_info *i) {          goto fail;      } -    n = i->fragment_size; +    n = (int) i->fragment_size;      setsockopt(i->app_fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)); -    n = i->fragment_size; +    n = (int) i->fragment_size;      setsockopt(i->thread_fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n));      return 0; @@ -1474,7 +1479,7 @@ int open(const char *filename, int flags, ...) {      if (flags & O_CREAT) {          va_start(args, flags);          if (sizeof(mode_t) < sizeof(int)) -            mode = va_arg(args, int); +            mode = (mode_t) va_arg(args, int);          else              mode = va_arg(args, mode_t);          va_end(args); diff --git a/src/utils/paplay.c b/src/utils/paplay.c index 9264a940..df2edf62 100644 --- a/src/utils/paplay.c +++ b/src/utils/paplay.c @@ -107,14 +107,14 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {      if (readf_function) {          size_t k = pa_frame_size(&sample_spec); -        if ((bytes = readf_function(sndfile, data, length/k)) > 0) -            bytes *= k; +        if ((bytes = readf_function(sndfile, data, (sf_count_t) (length/k))) > 0) +            bytes *= (sf_count_t) k;      } else -        bytes = sf_read_raw(sndfile, data, length); +        bytes = sf_read_raw(sndfile, data, (sf_count_t) length);      if (bytes > 0) -        pa_stream_write(s, data, bytes, pa_xfree, 0, PA_SEEK_RELATIVE); +        pa_stream_write(s, data, (size_t) bytes, pa_xfree, 0, PA_SEEK_RELATIVE);      else          pa_xfree(data); @@ -283,7 +283,7 @@ int main(int argc, char *argv[]) {              case ARG_VOLUME: {                  int v = atoi(optarg); -                volume = v < 0 ? 0 : v; +                volume = v < 0 ? 0U : (pa_volume_t) v;                  break;              } @@ -315,8 +315,8 @@ int main(int argc, char *argv[]) {          goto quit;      } -    sample_spec.rate = sfinfo.samplerate; -    sample_spec.channels = sfinfo.channels; +    sample_spec.rate = (uint32_t) sfinfo.samplerate; +    sample_spec.channels = (uint8_t) sfinfo.channels;      readf_function = NULL;  | 
